[
  {
    "path": ".gitattributes",
    "content": "* text=auto eol=lf\n*.svg       binary\n"
  },
  {
    "path": ".gitignore",
    "content": "*.diff\n*.err\n*.orig\n*.log\n*.rej\n*.swo\n*.swp\n*.vi\n*~\n*.sass-cache\n\n# OS or Editor folders\n.DS_Store\n.idea\n.cache\n.project\n.settings\n.tmproj\n.nvmrc\nsftp-config.json\nThumbs.db\n\n\n"
  },
  {
    "path": "1-js/01-getting-started/1-intro/article.md",
    "content": "# Pengenalan JavaScript\n\nMari kita lihat apa yang spesial dari JavaScript, apa saja yang bisa kita buat menggunakan JavaScript, dan teknologi apa yang cocok dengan JavaScript.\n\n## Apa itu JavaScript?\n\n*Javascript\" pada awalnya diciptakan untuk \"membuat halaman web menjadi hidup\".\n\nProgram yang ada dalam bahasa ini disebut *script*. Script ini bisa ditulis langsung ke dalam kode HTML dari sebuah web dan berjalan otomatis saat halaman dimuat.\n\nScript tersedia dan dieksekusi sebagai sebuah teks biasa. Script tidak membutuhkan persiapan khusus atau kompilasi untuk dijalankan.\n\nDalam hal ini, JavaScript sangat berbeda dari bahasa lain yang disebut [Java](https://en.wikipedia.org/wiki/Java_(programming_language)).\n\n```smart header=\"Kenapa disebut <u>Java</u>Script?\"\nSaat JavaScript diciptakan, nama awalnya adalah \"LiveScript\". Namun saat itu Java sudah sangat terkenal, sehingga akhirnya diputuskanlah bahwa memposisikan bahasa baru menjadi \"adik\" Java dapat membantu.\n\nSeiring waktu, JavaScript tumbuh menjadi bahasa yang sepenuhnya bebas dengan memiliki spesifikasi [ECMAScript](http://en.wikipedia.org/wiki/ECMAScript), dan sekarang JavaScript tak punya hubungan apa-apa dengan Java.\n```\n\nSekarang, JavaScript bisa berjalan tak hanya pada browser, tapi juga di server, atau di perangkat manapun yang memiliki program khusus [JavaScript engine](https://en.wikipedia.org/wiki/JavaScript_engine).\n\nBrowser punya engine yang tertanam didalamnya yang disebut \"JavaScript virtual machine\".\n\nTiap engine punya *codename*-nya sendiri. Misalnya:\n\n- [V8](https://en.wikipedia.org/wiki/V8_(JavaScript_engine)) -- di Chrome dan Opera.\n- [SpiderMonkey](https://en.wikipedia.org/wiki/SpiderMonkey) -- di Firefox.\n- ...Ada juga codename lain seperti \"Trident\" dan \"Chakra\" untuk versi berbeda dari IE, \"ChakraCore\" untuk Microsoft Edge, \"Nitro\" dan \"SquirrelFish\" untuk Safari, dll.\n\nIstilah di atas sebaiknya diingat karena akan sering digunakan dalam artikel para developer di internet. Kita akan menggunakannya juga. Misalnya, jika \"fitur X didukung V8\", kemungkinan ia bisa jalan di Chrome dan Opera.\n\n```smart header=\"Bagaimana engine bekerja?\"\n\nEngine sangat rumit. Tapi basicnya mudah.\n\n1. Engine (tertanam jika ia sebuah browser)bisa membaca (\"memparsing\") script.\n2. Lalu ia mengkonversi (\"mengkompilasi\") script tersebut menjadi bahasa mesin.\n3. Dan kemudian kode mesin berjalan, lumayan cepat.\n\nEngine melakukan optimisasi di setiap langkah proses. Dia bahkan memperhatikan script yang telah dikompilasi saat sedang berjalan, menganalisa data yang mengalir di dalam, dan melakukan optimisasi ke kode mesin berdasarkan pengetahuan itu.\n```\n\n## Apa yang bisa dilakukan *in-browser JavaScript*?\n\nJavaScript modern merupakan bahasa pemrograman yang \"aman\". Ia tidak menyebabkan akses tingkat-rendah ke memory atau CPU, karena memang awalnya dibuat untuk browser, yang tentunya tidak membutuhkan hal tersebut.\n\nKemampuan JavaScript sangat tergantung pada lingkungan tempat ia berjalan. Misalnya, [Node.js](https://wikipedia.org/wiki/Node.js) mendukung function yang memungkingkan JavaScript melakukan baca/tulis file apapun, melakukan permintaan jaringan, dsb.\n\n*In-browser JavaScript* bisa melakukan apapun terkait manipulasi halaman web, interaksi dengan pengguna, dan webserver.\n\nMisalnya, *in-browser JavaScript* mampu:\n\n- Menambah HTML baru ke sebuah halaman, mengganti isinya, memodifikasi gayanya.\n- Bereaksi terhadap aktifitas pengguna, berjalan saat mouse diklik, pointer digerakkan, tombol ditekan.\n- Mengirim permintaan jaringan ke remote server, mengunduh dan mengunggah file (disebut teknologi [AJAX](https://en.wikipedia.org/wiki/Ajax_(programming)) dan [COMET](https://en.wikipedia.org/wiki/Comet_(programming))).\n- Memperoleh and menset cookie, bertanya ke pengunjung, menampilkan pesan.\n- Menyimpan data pada client-side (\"local storage\").\n\n## Apa yang TIDAK BISA dilakukan *in-browser JavaScript*?\n\nKemampuan JavaScript yang ada di dalam browser terbatas demi keamanan pengguna. Tujuannya supaya mencegah halaman web berbahya mengakses informasi pribadi atau merusak data pengguna.\n\nContoh keterbatasan tersebut meliputi:\n\n- Javascript didalam sebuah halaman web seharusnya tidak dapat membaca/mengubah file didalam hardisk semaunya.\n\n    Browser-browser modern memperbolehkan JavaScript mengakses file, tapi aksesnya dibatasi dan tersedia hanya jika pengguna melakukan hal tertentu, misalnya seperti \"menjatuhkan\" file ke dalam jendela browser atau memilih file via tag `<input>`.\n\n    Ada beberapa cara untuk berinteraksi dengan kamera/mikrofon dan perangkat-perangkat lainnya, namun mereka butuh ijin khusus pengguna. Jadi sebuah halaman yang memiliki JavaScript tidak bisa mengaktifkan web-camera, memantau sekeliling dan mengirim informasinya ke [NSA] secara diam-diam(https://en.wikipedia.org/wiki/National_Security_Agency).\n- Tab/jendela yang berbeda umumnya tidak ada hubungan sama sekali. Terkadang jendela yang berbeda bisa saling berhubungan juga, misalnya ketika satu jendela menggunakan JavaScript untuk membuka jendela lainnya. Tapi meski demikian, JavaScript dari suatu halaman tak boleh mengakses halaman lainnya jika mereka berasal dari situs yang berbeda (dari domain, protokol, atau port berbeda).\n\n    Ini disebut \"Same Origin Policy\". Untuk melakukan hal tersebut, *kedua halaman* harus sepakat terhadap adanya pertukaran data dan memiliki kode JavaScript khusus yang melakukan hal tersebut. Kita akan membahasnya nanti dalam tutorial ini.\n\n    Pembatasan ini pun demi keselamatan pengguna. Sebuah halaman dari `http://anysite.com` yang dibuka pengguna tidak akan bisa mengakses tab browser lainnya dengan URL `http://gmail.com` dan mencuri informasinya.\n- JavaScript bisa dengan mudah berinteraksi secara online ke server di mana halaman berasal. Tapi kemampuannya menerima data dari situs/domain lain dilumpuhkan. Meskipun mampu, ia butuh persetujuan explisit (yang diexpresikan dalam HTTP header) dari sisi remote. Sekali lagi, itu merupakan pembatasan keamanan.\n\n![](limitations.svg)\n\nPembatasan macam ini tidak ada jika JavaScript digunakan di luar browser, misalnya di server. Browser-browser modern juga memperbolehkan plugin/ekstensi yang membutuhkan ijin tambahan.\n\n## Apa yang membuat JavaScript unik?\n\nPaling tidak ada *tiga* hal unik dari JavaScript:\n\n```compare\n+ Integrasi penuh dengan HTML/CSS.\n+ Hal sederhana diselesaikan dengan sederhana.\n+ Dukungan dari mayoritas web browser dan aktif secara default.\n```\nJavaScript merupakan satu-satunya teknologi browser yang mengkombinasikan ketiga poin di atas.\n\nItulah yang membuat JavaScript unik. Itulah kenapa JavaScript menjadi alat yang paling sering untuk membuat antarmuka browser.\n\nKatanya, JavaScript juga bisa dipakai untuk membuat aplikasi server, mobile, dsb.\n\n## Bahasa \"di atas\" JavaScript\n\nSintaks JavaScript tidak memenuhi kebutuhan setiap orang. Masing-masing orang ingin fitur yang berbeda-beda.\n\nItu wajar, karena proyek dan persyaratan tiap orang berbeda-beda.\n\nAkhir-akhir ini muncul banyak bahasa baru, yang *ditranspile* (dikonversi) ke JavaScript sebelum dijalankan di browser.\n\nTools modern membuat transpilasi sangat cepat dan transparan, yang memungkinkan para developer menulis kodenya dalam bahasa lain dan mengautokonversi itu \"di balik layar\".\n\nContoh bahasa yang dimaksud:\n\n- [CoffeeScript](http://coffeescript.org/) merupakan \"syntactic sugar\" dari JavaScript. Dia memperkenalkan syntax yang lebih pendek, memungkingkan kita menulis kode lebih bersih dan lebih presisi. Biasanya, Ruby devs menyukainya.\n- [TypeScript](http://www.typescriptlang.org/) berfokus pada penambahan \"strict data typing\" yang menyederhanakan pengembangan dan dukungan sistem yang komplex. Ia dikembangkan oleh Microsoft.\n- [Flow](http://flow.org/) juga menambahkan data typing, tapi dalam cara berbeda. Dikembangkan oleh Facebook.\n- [Dart](https://www.dartlang.org/) ialah bahasa mandiri yang punya engine sendiri yang berjalan di lingkungan non-peramban (seperti mobile apps), tapi bisa juga ditranspile ke JavaScript. Dikembangkan oleh Google.\n- [Brython](https://brython.info/) adalah transpiler python untuk Javascript yang memperbolehkan untuk menulis kode aplikasi didalam Python murni tanpa Javascript.\n- [Kotlin](https://kotlinlang.org/docs/reference/js-overview.html) adalah sebuah bahasa pemograman modern, ringkas dan  aman yang dapat ditargetkan untuk browser atau Node.\n\nMasih banyak lagi. Tentunya, jika kita menggunakan salah satu bahasa yang ditranspile tersebut, kita sebaiknya juga paham JavaScript untuk mengerti apa yang mereka lakukan.\n\n## Kesimpulan\n\n- JavaScript awalnya diciptakan sebagai bahasa khusus browser, namun sekarang banyak digunakan di lingkungan lain.\n- Sekarang, JavaScript mempunyai posisi unik sebagai bahasa browser paling banyak diadopsi dengan integrasi penuh dengan HTML/CSS.\n- Ada banyak bahasa yang \"ditranspile\" ke JavaScript dan menyediakan fitur tertentu. Disarankan untuk mempelajari mereka juga, minimal sebentar, setelah menguasai JavaScript.\n"
  },
  {
    "path": "1-js/01-getting-started/2-manuals-specifications/article.md",
    "content": "# Manual dan spesifikasi\n\nBuku ini adalah _tutorial_. Tujuannya membantu kamu memahami bahasa ini (Javascript) pelan-pelan. Tapi sekali kamu akrab atau familiar dengan dasarnya, kamu juga membutuhkan dari sumber-sumber lain.\n\n## Spesifikasi\n\n[Spesifikasi ECMA-262](https://www.ecma-international.org/publications/standards/Ecma-262.htm) berisi informasi formal, detil, and mendalam tentang JavaScript. Ia mendefisikan bahasa ini.\n\nTapi karena menjadi formal, ia sulit dipahami di awal. Jadi jika kamu butuh sumber informasi terpercaya tentang detil bahasa, spesifikasi ini tempat yang tepat. Tapi ini bukan untuk penggunaan harian.\n\nVersi spesifikasi baru dirilis tiap tahun. Di antara rilis ini, draft spesifikasi terakhir ada di <https://tc39.es/ecma262/>.\n\nUntuk membaca tentang fitur terkini, termasuk yang \"hampir menjadi standar\" (disebut \"stage 3\"), lihat proposalnya di <https://github.com/tc39/proposals>.\n\nJuga, jika kamu dalam pengembangan untuk peramban, maka ada spek lain yang dibahas di [bagian kedua](info:browser-environment) di tutorial ini.\n\n## Manual\n\n-   **Referensi JavaScript MDN (Mozilla)** ialah manual dengan informasi dan contoh lain. Di sana bagus untuk mendapat informasi mendalam tentang metode, fungsi bahasa, dll.\n\n    Kamu bisa cari di <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference>.\n\n    Meski, sering lebih bagus menggunakan pencarian internet. Pakai \"MDN [term]\" di query, misal <https://google.com/search?q=MDN+parseInt> untuk mencari fungsi `parseInt`.\n\n-   **MSDN** – Manual Microsoft dengan banyak informasi, termasuk JavaScript (sering dirujuk sebagai JScript). Jika kamu butuh sesuatu yang spesifik ke Internet Explorer, lebih baik pergi ke: <http://msdn.microsoft.com/>.\n\n    Juga, kamu bisa menggunakan pencarian internet dengan frasa seperti \"RegExp MSDN\" atau \"RegExp MSDN jscript\".\n\n## Tabel kompatibilitas\n\nJavaScript merupakan bahasa berkembang, fitur baru ditambah secara reguler.\n\nUntuk melihat dukungan mereka pada engine berbasis peramban dan lainnya, lihat:\n\n-   <http://caniuse.com> - tabel dukungan per-fitur, misal untuk melihat engine mana yang mendukung fungsi kryptografi modern: <http://caniuse.com/#feat=cryptography>.\n-   <https://kangax.github.io/compat-table> - tabel dengan fitur dan engine bahasa yang mendukung atau yang tidak mendukung.\n\nSemua sumber ini berguna di pengembangan nyata, karena mereka berisi informasi berharga tentang detil bahasa, dukungan mereka dll.\n\nSilakan ingat mereka (atau laman ini) saat kamu butuh informasi mendalam tentang fitur tertentu.\n"
  },
  {
    "path": "1-js/01-getting-started/3-code-editors/article.md",
    "content": "# Editor kode\n\nEditor kode adalah tempat programmer menghabiskan usianya.\n\nAda dua tipe utama editor kode: IDE dan editor ringan. Kebanyakan orang menggunakan satu tool saja dari setiap tipe.\n\n## IDE\n\nIstilah [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) (Integrated Development Environment) mengacu kepada editor mumpuni dengan banyak fitur yang biasanya beroperasi di atas \"seluruh proyek.\" Dilihat dari namanya, ia bukan hanya sekedar editor biasa, tapi sebuah \"lingkungan pengembangan\" berskala besar.\n\nSatu IDE meload proyek (yang bisa berupa banyak file), memungkingkan navigasi antar file, menyediakan autocompletion berdasarkan seluruh proyek (tak hanya file terbuka), dan berintegrasi dengan sistem manajemen versi (seperti [git](https://git-scm.com/)), lingkungan pengujian, dan hal-hal \"level proyek\" lainnya.\n\nJika kamu belum memilih satu IDE, pertimbangkan opsi-opsi berikut:\n\n- [Visual Studio Code](https://code.visualstudio.com/) (lintas-platform, gratis).\n- [WebStorm](http://www.jetbrains.com/webstorm/) (lintas-platform, berbayar).\n\nUntuk Windows, ada juga \"Visual Studio\", jangan dipusingkan dengan \"Visual Studio Code.\" \"Visual Studio\" merupakan editor khusus Windows yang keren dan berbayar, sangat cocok untuk platform .NET. Ia bagus juga untuk JavaScript. Lalu ada juga versi gratisnya [Visual Studio Community](https://www.visualstudio.com/vs/community/).\n\nBanyak IDE berbayar, tapi punya masa percobaan. Biasanya harganya tak seberapa dibanding gaji pengembang berkualitas, jadi pilihlah yang terbaik untukmu.\n\n## Editor ringan\n\n\"Editor ringan\" tak semumpuni IDE, tapi mereka cepat, elegan, dan simpel.\n\nMereka digunakan terutama untuk membuka dan mengedit file secara instan.\n\nPerbedaan utama antara \"editor ringan\" dan \"IDE\" adalah IDE bekerja pada level proyek, jadi IDE meload banyak data di awal, menganalisa struktur proyek jika dibutuhkan dan lain sebagainya. Editor ringan jauh lebih cepat jika kita cuma membutuhkan hanya satu file.\n\nPada praktiknya, editor ringan bisa punya banyak plugin termasuk syntax analyzers dan autocompleters level direktori, jadi tak ada batasan ketat antara editor ringan dan IDE.\n\nOpsi-opsi berikut patut anda perhatikan:\n\n- [Atom](https://atom.io/) (lintas-platform, gratis).\n- [Visual Studio Code](https://code.visualstudio.com/) (lintas-platform, gratis).\n- [Sublime Text](http://www.sublimetext.com) (lintas-platform, shareware).\n- [Notepad++](https://notepad-plus-plus.org/) (Windows, gratis).\n- [Vim](http://www.vim.org/) dan [Emacs](https://www.gnu.org/software/emacs/) sangat keren juga jika kamu tahu cara pakainya.\n\n## Jangan berdebat\n\nDaftar editor di atas merupakan barang yang sudah biasa saya atau teman-teman saya para pengembang profesional gunakan selama ini dengan gembira.\n\nAda banyak editor bagus lainnya di dunia kita yang besar ini. Silakan pilih satu yang paling kamu suka.\n\nPilihan editor, sama seperti tool lainnya, bersifat individual dan tergantung pada proyek, kebiasaan, dan preferensi personal.\n"
  },
  {
    "path": "1-js/01-getting-started/4-devtools/article.md",
    "content": "# Konsol pengembang\n\nYang namanya kode selalu rentan error. Mudah sekali bagimu bikin error... Benar kan? Kamu *tidak akan pernah* luput dari error, itu karena kamu manusia, bukan [robot](https://en.wikipedia.org/wiki/Bender_(Futurama)).\n\nTapi di dalam peramban, error tidak terlihat ke pengguna secara default. Jadi, kalau ada yang salah di script, itu tidak kelihatan mata kita dan kita sudah memperbaiki itu.\n\nSupaya bisa melihat error dan memperoleh informasi berfaedah lainnya dari script, \"tools pengembang\" ditanamkan di dalam peramban.\n\nKebanyakan pengembang memakai Chrome atau Firefox untuk pengembangan karena tools pengembangan yang mereka punya paling mantap. Peramban lain punya juga koq, ada with special features, but are usually playing \"catch-up\" to Chrome or Firefox. So most developers have a \"favorite\" browser and switch to others if a problem is browser-specific.\n\nTools pengembang mengandung banyak manfaat; mereka punya banyak fitur. Untuk memulainya, kita akan belajar cara membuka mereka, mencari error, dan menjalankan perintah JavaScript.\n\n## Google Chrome\n\nBuka laman [bug.html](bug.html).\n\nAda satu error di dalam kode JavaScript di situ. Ia tersembunyi dari mata pengunjung biasa, mari kita buka tools pengembang untuk melihatnya.\n\nTekan `key:F12` atau, kalau kamu pakai Mac, tekan `key:Cmd+Opt+J`.\n\nTools pengembang akan terbuka pada Console tab secara default.\n\nNanti tampilannya seperti ini:\n\n![chrome](chrome.png)\n\nTampilan persisnya tools pengembang tergantung versi Chrome kamu. Ia berubah dari masa ke masa tapi tetap serupa.\n\n- Di sini kita bisa melihat pesan error berwarna merah. Di sini, scriptnya mengandung perintah asing \"lalala\".\n- Di kanan, ada link yang bisa diklik ke sumber `bug.html:12` dengan nomor baris di mana error itu muncul.\n\nDi bawah pesan error, ada simbol `>` berwarna biru. Ia menandakan \"command line\" di mana kita bisa mengetik perintah JavaScript. Tekan `key:Enter` untuk menjalankannya.\n\nSekarang kita bisa melihat error, dan itu sudah cukup untuk permulaan. Kita nanti akan kembali ke tools pengembang dan mengcover debugging lebih dalam di bab <info:debugging-chrome>.\n\n```smart header=\"Multi-line input\"\nUsually, when we put a line of code into the console, and then press `key:Enter`, it executes.\n\nTo insert multiple lines, press `key:Shift+Enter`. This way one can enter long fragments of JavaScript code.\n```\n\n## Firefox, Edge, dan lainnya\n\nBanyak peramban lain memakai `key:F12` untuk membuka tools pengembang.\n\nLook & feel mereka hampir mirip. Sekali kamu tahu cara memakainya (kamu bisa mulai dengan Chrome), kamu bisa dengan mudah ganti dari satu ke yang lain.\n\n## Safari\n\nSafari (peramban Mac, tidak didukung Windows/Linux) agak sedikit spesial di sini. Kita harus mengaktifkan \"Develop menu\" terlebih dulu.\n\nBuka Preferences dan pergi ke \"Advanced\" pane. Di sana ada checkbox di sebelah bawah:\n\n![safari](safari.png)\n\nSekarang `key:Cmd+Opt+C` bisa mentoggle konsol. Lalu, menu \"Develop\" muncul pada menu item di atas. Ia punya banyak perintah dan opsi.\n\n## Kesimpulan\n\n- Tools pengembang memungkinkan kita melihat error, menjalankan perintah, memeriksa variabel, dan sebagainya.\n- Mereka bisa dibuka dengan `key:F12` untuk kebanyakan peramban di Windows. Chrome di Mac dengan `key:Cmd+Opt+J`, Safari `key:Cmd+Opt+C` (harus diaktifkan terlebih dulu).\n\nKini kita sudah menyiapkan lingkungannya. Di seksi berikutnya, kita akan terjun ke JavaScript.\n"
  },
  {
    "path": "1-js/01-getting-started/4-devtools/bug.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  There is an error in the script on this page.\n  <script>\n    lalala\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/01-getting-started/index.md",
    "content": "# Pengenalan\n\nTentang bahasa JavaScript dan lingkungan pengembangannya."
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    alert( \"I'm JavaScript!\" );\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/solution.md",
    "content": "\r\n[html src=\"index.html\"]\r\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    alert( \"I'm JavaScript!\" );\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/1-hello-alert/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tampilkan alert\n\nBuat laman yang menampilkan pesan \"I'm JavaScript!\".\n\nLakukan dalam sandbox, atau di hard drive kamu, tak masalah, pastikan bisa berjalan.\n\n[demo src=\"solution\"]\n\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/alert.js",
    "content": "alert(\"I'm JavaScript!\");"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script src=\"alert.js\"></script>\n\n</body>\n\n</html>"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/solution.md",
    "content": "Kode HTML:\n\n[html src=\"index.html\"]\n\nUntuk file `alert.js` di dalam folder yang sama:\n\n[js src=\"alert.js\"]\n\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/2-hello-alert-ext/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tampilkan alert dengan script external\n\nAmbil solusi dari tugas sebelumnya <info:task/hello-alert>. Modifikasi itu dengan mengextrak konten script ke file external `alert.js`, yang berada di dalam folder yang sama.\n\nBuka lamannya, pastikan alertnya bekerja.\n"
  },
  {
    "path": "1-js/02-first-steps/01-hello-world/article.md",
    "content": "# Hello, world!\n\nBagian dari tutorial ini ialah tentang inti JavaScript itu sendiri.\n\nTapi kita butuh lingkungan kerja untuk menjalankan scripts kita dan, karena buku ini online, peramban adalah pilihan yang baik. Kita akan menjaga supaya jumlah perintah yang spesifik peramban (seperti `alert`) seminimum mungkin sehingga kamu tak boros waktu di situ jika kamu berencana untuk fokus ke lingkungan lain (seperti Node.js). Kita akan fokus ke JavaScript di peramban dalam [bagian selanjutnya](/ui) dari tutorial ini.\n\nJadi pertama, kita lihat bagaimana kita menyisipkan script ke laman web. Untuk lingkungan (seperti Node.js), kamu bisa mengeksekusi script itu dengan perintah seperti `\"node my.js\"`.\n\n\n## Tag \"script\"\n\nProgram JavaScript bisa disisipkan ke dalam bagian mana saja dari dokumen HTML dengan bantuan tag `<script>`.\n\nContoh:\n\n```html run height=100\n<!DOCTYPE HTML>\n<html>\n\n<body>\n\n  <p>Kode ini ditulis sebelum skrip...</p>\n\n*!*\n  <script>\n    alert( 'Hello, world!' );\n  </script>\n*/!*\n\n  <p>...Kode ini ditulis setelah skrip.</p>\n\n</body>\n\n</html>\n```\n\n```online\nKamu bisa menjalankan contohnya dengan mengklik tombol \"Play\" di sebelah ujung kanan-atas dari box di atas.\n```\n\nTag `<script>` mengandung kode JavaScript yang otomatis dieksekusi ketika peramban memproses tag.\n\n\n## Markup modern\n\nTag `<script>` punya beberapa attribut yang jarang dipakai akhir-akhir ini tapi masih bisa ditemukan dalam kode lama:\n\nAtribut `type`: <code>&lt;script <u>type</u>=...&gt;</code>\n: Standar HTML lawas, HTML4, mengharuskan script memiliki `type`. Biasanya `type=\"text/javascript\"`. Sekarang sudah tak diperlukan. Selain itu, standar HTML modern, HTML5, mengubah total makna atribut ini. Sekarang, ia bisa digunakan untuk modul JavaScript. Tapi itu topik berat, kita akan membahas modul di bagian lain dari tutorial ini.\n\nAtribut `language`: <code>&lt;script <u>language</u>=...&gt;</code>\n: Atribut ini untuk menampilkan bahasa script. Atribut ini tak lagi dibutuhkan karena JavaScript adalah bahasa default. Tak usah menggunakan itu lagi.\n\nKomen sebelum dan setelah script.\n: Di dalam buku dan panduan jadul, kamu mungkin menemukan komen di dalam tag `<script>`, seperti ini:\n\n    ```html no-beautify\n    <script type=\"text/javascript\"><!--\n        ...\n    //--></script>\n    ```\n\n    Trik ini tak lagi dipakai di JavaScript modern. Komen ini menyembunyikan kode JavaScript dari peramban tua yang tak tahu cara memproses tag `<script>`. Oleh karena peramban yang dirilis 15 tahun terakhir tak punya masalah terkait ini, komen macam ini bisa membantumu mengidentifikasi kode yang sangat jadul.\n\n\n## Script External\n\nJika kita punya banyak kode JavaScript, kita bisa menaruhnya di dalam file berbeda.\n\nFile script ditempel ke HTML dengan atribut `src`:\n\n```html\n<script src=\"/path/to/script.js\"></script>\n```\n\nDi sini, `/path/to/script.js` adalah jalur absolut ke file script dari root sitius. Kamu juga bisa menyediakan jalur relatif dari laman ini. Misalnya, `src=\"script.js\"` berarti file `\"script.js\"` dalam folder saat ini.\n\nKamu bisa memasang URL penuh juga. Misalnya:\n\n```html\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\"></script>\n```\n\nUntuk menempelkan beberapa script, gunakan tag berlapis:\n\n```html\n<script src=\"/js/script1.js\"></script>\n<script src=\"/js/script2.js\"></script>\n…\n```\n\n```smart\nAturannya, cuma script paling simpel yang ditaruh di dalam HTML. Script lebih rumit ditaruh di file terpisah.\n\nKeuntungan file terpisah ialah peramban akan mengunduhnya dan menyimpannya di [cache](https://en.wikipedia.org/wiki/Web_cache)-nya.\n\nLaman lain yang merujuk ke script yang sama akan mengambilnya dari cache ketimbang mengunduhnya, jadi sebenarnya file hanya diunduh sekali saja.\n\nItu mengurangi trafik dan membuat laman jadi lebih cepat.\n```\n\n````warn header=\"Jika `src` diset, konten script diabaikan.\"\nTag `<script>` tunggal tak bisa punya atribut `src` dan kode di dalamnya bersamaan.\n\nIni tak akan berjalan:\n\n```html\n<script *!*src*/!*=\"file.js\">\n  alert(1); // konten diabaikan karena 'src' telah di set\n</script>\n```\n\nKita harus memilih antara `<script src=\"…\">` external atau `<script>` external.\n\nContoh di atas bisa dipecah menjadi dua script:\n\n```html\n<script src=\"file.js\"></script>\n<script>\n  alert(1);\n</script>\n```\n````\n\n## Kesimpulan\n\n- Kita bisa menggunakan tag `<script>` untuk menambah kode JavaScript ke laman.\n- Atribut `type` dan `language` tak wajib.\n- Script di file external bisa disisipkan dengan `<script src=\"path/to/script.js\"></script>`.\n\n\nMasih banyak lagi yang harus dipelajari tentang script peramban dan interaksi mereka dengan laman web. Tapi harap diingat bahwa bagian ini dari tutorial ini dikhususkan hanya ke bahasa JavaScript, jadi kita tak akan membahas implementasi Javascript yang spesifik peramban. Kita akan menggunakan peramban hanya sebagai alat untuk menjalankan JavaScript, yang nyaman untuk bacaan online.\n"
  },
  {
    "path": "1-js/02-first-steps/02-structure/article.md",
    "content": "# Struktur kode\n\nHal pertama yang kita akan pelajari ialah membangun blok kode.\n\n## Pernyataan\n\nPernyataan ialah konsep dan perintah syntax yang mejalankan aksi.\n\nKita sudah melihat satu pernyataan, `alert('Hello, world!')`, yang menampilkan pesan \"Hello, world!\".\n\nKita bisa memiliki sebanyak apapun pernyataan dalam kode kita. Pernyataan bisa dipisah menggunakan titik koma.\n\nMisalnya, di sini kita memecah \"Hello World\" menjadi dua alert:\n\n```js run no-beautify\nalert('Hello'); alert('World');\n```\n\nBiasanya, pernyataan ditulis dalam baris terpisah supaya kode lebih mudah dibaca:\n\n```js run no-beautify\nalert('Hello');\nalert('World');\n```\n\n## Titik koma [#semicolon]\n\nTitik koma bisa dibuang dalam banyak kasus jika ada jeda baris.\n\nIni juga akan berjalan:\n\n```js run no-beautify\nalert('Hello')\nalert('World')\n```\n\nDi sini, JavaScript menginterpretasi jeda baris sebagai titik koma \"implisit\". Ini disebut [penyisipan titik koma otomatis](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion).\n\n**Dalam banyak kasus, sebuah garis baru mengimplikasikan titik koma. Tapi \"dalam banyak kasus\" tak \"selalu\" begitu!**\n\nAda kasus ketika garis baru tidak berarti titik koma. Misalnya:\n\n```js run no-beautify\nalert(3 +\n1\n+ 2);\n```\n\nOutput dari kode itu adalah `6` karena JavaScript tak menyisipkan titik koma di sini. Sudah jelas sekali bahwa barisnya selesai dengan tanda plus `\"+\"`, sehingga itu menjadi \"expresi tak lengkap\", jadi tak butuh titik koma. Dan dalam hal ini memang seperti itu.\n\n**Tapi ada situasi di mana JavaScript \"gagal\" mengasumsi titik koma di mana itu benar-benar dibutuhkan.**\n\nGalat yang muncul pada kasus ini agak sulit dicari dan dibetulkan.\n\n````smart header=\"Contoh galat\"\nJika kamu penasaran untuk melihat contoh konkrit dari galat ini, cek kode ini:\n\n```js run\nalert(\"Hello\");\n\n[1, 2].forEach(alert);\n```\n\nUntuk sekarang tak usah memikirkan makna kurung siku `[]` dan `forEach`. Kita akan mempelajari mereka nanti. Untuk sekarang, ingat hasil kode tersebut: yaitu `1` lalu `2`.\n\nSekarang, ayo kita tambahkan `alert` sebelum kodenya *tanpa* diikuti titik koma:\n\n```js run no-beautify\nalert(\"Hello\")\n\n[1, 2].forEach(alert);\n```\n\nSekarang jika kita menjalankan kodenya, hanya `alert` pertama yang tampil dan kemudian galat!\n\nTapi semua akan baik-baik saja jika kita menambahkan titik koma setelah `alert`:\n```js run\nalert(\"All fine now\");\n\nIf we run this code, only the first `Hello` shows (and there's an error, you may need to open the console to see it). There are no numbers any more.\n\nSekarang kita punya pesan \"All fine now\" diikuti dengan `1` dan `2`.\n\n\nGalat muncul pada varian tanpa titik koma karena JavaScript tak mengasumsikan titik koma sebelum kurung siku `[...]`.\n\nJadi, karena titik koma tidak otomatis disisipkan, kode di contoh pertama diperlakukan sebagai pernyataan tunggal. Inilah cara engine melihatnya:\n\n```js run no-beautify\nalert(\"Hello\")[1, 2].forEach(alert);\n```\n\nTapi itu harus jadi dua pernyataan terpisah, bukan satu. Penyatuan macam ini salah pada kasus ini, makanya galat. Ini bisa terjadi dalam situasi lain.\n````\n\nKami sarankan menaruh titik koma di antara pernyataan meski mereka dipisahkan garis baru. Ini aturan yang diterima secara luas oleh komunitas. Harap diingat sekali lagi bahwa -- *bisa saja* menanggalkan titik koma di banyak kesempatan. Tapi akan lebih aman -- terutama untuk pemula -- untuk menggunakan mereka.\n\n## Komentar [#komentar pada sebuah kode]\n\nSeiring waktu berjalan, program menjadi lebih rumit. Dan dibutuhkan *komen* yang menjelaskan kode apa itu dan kenapa.\n\nKomen bisa ditaruh di mana saja dari script. Dan tidak berpengaruh ke eksekusi karena engine mengabaikan mereka.\n\n**Satu-baris komen bermula dengan dua karakter slash `//`.**\n\nSisa barisnya adalah komen. Ia bisa memenuhi satu baris sendiri atau mengikuti pernyataan.\n\nSeperti di sini:\n```js run\n// Komen ini menghuni satu baris sendiri\nalert('Hello');\n\nalert('World'); // Komen ini mengikuti pernyataan\n```\n\n**Komen multiline bermula dengan garis miring dan bintang <code>/&#42;</code> dan berakhir dengan bintang dan garis miring <code>&#42;/</code>.**\n\nSeperti ini:\n\n```js run\n/* Contoh dengan dua pesan.\nIni komen multiline.\n*/\nalert('Hello');\nalert('World');\n```\n\nKonten komen diabaikan, jadi jika menaruh kode di dalam <code>/&#42; ... &#42;/</code>, ia tidak akan dieksekusi.\n\nKadang sangat berguna jika kita bisa menonaktifkan sementara sebagian kode:\n\n```js run\n/* Mengkomen kode\nalert('Hello');\n*/\nalert('World');\n```\n\n```smart header=\"Gunakan hotkey!\"\nDi banyak editor, sebaris kode bisa dikomen dengan menekan hotkey `key:Ctrl+/` untuk komen baris-tunggal dan sesuatu macam `key:Ctrl+Shift+/` -- untuk komen multibaris (pilih sepotong kode dan tekan hotkeynya). Untuk Mac, coba `key:Cmd` ketimbang `key:Ctrl`.\n```\n\n````warn header=\"Komen bersarang tidak didukung!\"\nTidak boleh ada `/*...*/` di dalam `/*...*/` yang lain.\n\nKode begini akan berakhir galat:\n\n```js run no-beautify\n/*\n  /* komen bersarang ?!? */\n*/\nalert( 'World' );\n```\n````\n\nSilakan, jangan ragu mengkomen.\n\nKomen meningkatkan kode footprint garis besar, tapi itu bukan masalah sama sekali. Ada banyak tools yang meminifikasi kode sebelum dipublikasi ke production server. Mereka menghapus komen, jadi mereka tidak tampil di script yang berjalan. Selain itu, komen tidak punya efek negatif pada production sama sekali.\n\nDi akhir tutorial ini akan ada bab <info:code-quality> yang juga menerangkan cara menulis komen yang lebih baik.\n"
  },
  {
    "path": "1-js/02-first-steps/03-strict-mode/article.md",
    "content": "# The modern mode, \"use strict\"\n\nSelama ini, JavaScript tumbuh tanpa isu kompatibilitas. Fitur baru ditambahkan tanpa mengubah fungsionalitas yang sudah ada.\n\nKeuntungannya adalah kode yang sudah ada tidak rusak. Tapi jeleknya adalah satu keputusan salah atau cacat yang dibuat oleh pembuat JavaScript akan menetap selamanya.\n\nInilah yang terjadi hingga tahun 2009 ketika ECMAScript 5 (ES5) muncul. Fitur baru ditambah dan beberapa kode yang ada diubah. Supaya kode lama tetap berjalan, kebanyakan modifikasi seperti ini secara default mati. Kamu harus mengaktifkan mereka secara explisit menggunakan directive special: `\"use strict\"`.\n\n## \"use strict\"\n\nDirective tersebut mirip string: `\"use strict\"` atau `'use strict'`. Jika itu diletakkan paling atas dari script, seluruh script akan bekerja secara \"modern\".\n\nMisalnya:\n\n```js\n\"use strict\";\n\n// this code works the modern way\n...\n```\n\nKita akan mempelajari fungsi (cara mengelompokkan perintah) segera. Melihat ke depan, ingatlah bahwa `\"use strict\"` bisa ditaruh di bagian awal fungsi. Itu membuat strict mode aktif hanya di dalam fungsi itu. Tapi biasanya, orang memakai itu untuk seluruh script.\n\n\n````warn header=\"Yakinkan bahwa \\\"use strict\\\" berada paling atas\"\nPastikan `\"use strict\"` berada paling atas dari script kamu, kalau tidak strict mode tidak akan aktif.\n\nStrict mode tidak aktif di sini:\n\n```js no-strict\nalert(\"some code\");\n// \"use strict\" di sini diabaikan--dia harus berada paling atas\n\n\"use strict\";\n\n// strict mode tidak aktif\n```\n\nHanya komen yang muncul di atas `\"use strict\"`.\n````\n\n```warn header=\"Tidak ada cara untuk membatalkan `use strict`\"\nTak ada directive seperti `\"no use strict\"` yang merevert engine ke kelakuan lama.\n\nSekali kita masuk strict mode, tak ada jalan kembali.\n```\n\n## Konsol pengembang\n\nKetika kamu menggunakan konsol pengembang [developer console](info:devtools) untuk menjalankan kode, perlu diingat pada dasarnya konsol pengembang tidak menggunakan `use strict`.\n\nKadang, ketika menggunakan `use strict`, kamu akan mendapat hasil yang salah.\n\nJadi, bagaimana seharusnya mengunakan `use strict` didalam konsol?\n\nPertama, kamu bisa menekan tombol `key:Shift+Enter` untuk memasukan beberapa baris kode dan masukan `use strict` di paling atas, seperti ini:\n\n```js\n'use strict'; <Shift+Enter untuk baris baru>\n//  ...Kodemu\n<Tekan enter untuk menjalankan>\n```\n\nIni bekerja pada kebanyakan peramban, seperti Firefox dan Chrome.\n\nJika tidak berjalan, seperti di browser lama, ada sebuah cara untuk memastikan penggunaan `use strict`. Masukan kodenya kedalam sebuah pembungkus:\n\n```js\n(function() {\n  'use strict';\n\n  // ...Kode lainnya disini...\n})()\n```\n\n## Haruskah kita menggunakan \"use strict\"?\n\nPertanyaannya sudah cukup jelas, tapi ternyata tidak.\n\nSeseorang bisa saja merekomendasikan memulai skrip dengan menggunakan `\"use strict\"`...Tapi apakah kamu tahu apa keren?\n\nJavascript modern mendukung kelas atau *classes* dan modul atau *modules* - struktur bahasa tingkat tinggi (kita akan belajar nanti), yang dapat mengaktifkan `use strict` secara otomatis. Jadi kita tidak perlu untuk menambahkan instruksi `\"use strict\"`, jika kita menggunakannya.\n\n**Jadi, untuk sekarang `\"use strict\";` adalah hal yang perlu ada di awal dari skrip kamu. Nanti, ketika seluruh kodemu telah menggunakan kelas dan modul, kamu bisa menghilangkannya**\n\nUntuk sekarang, kita harus mengetahui tentang `use strict` secara dasar.\n\nDi bagian selanjutnya, selagi kita belajar tentang fitur jadi Javascript, kita akan melihat beberapa perbedaan diantara *strict mode* dan mode lama. Beruntungnya, tidak dapat terdapat banyak perbedaan dan sebenarnya keduanya sangat bermanfaat.\n\nSeluruh contoh di tutorial ini menggunakan *strict mode* kecuali (sangat jarang) dituliskan sebaliknya."
  },
  {
    "path": "1-js/02-first-steps/04-variables/1-hello-variables/solution.md",
    "content": "Dalam kode di bawah, tiap baris berkorespondensi ke item di dalam daftar tugas.\n\n```js run\nlet admin, name; // bisa mendeklarasikan dua variabel sekaligus\n\nname = \"John\";\n\nadmin = name;\n\nalert( admin ); // \"John\"\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/1-hello-variables/task.md",
    "content": "nilai penting: 2\n\n---\n\n# Bekerja dengan variabel\n\n1. Deklarasikan dua variabel: `admin` and `name`.\n2. Assign nilai `\"John\"` ke `name`.\n3. Kopi nilai dari `name` ke `admin`.\n4. Tampilkan nilai `admin` menggunakan `alert` (harus beroutput \"John\").\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/2-declare-variables/solution.md",
    "content": "## Variabel untuk planet kita\n\nItu simpel:\n\n```js\nlet ourPlanetName = \"Earth\";\n```\n\nIngat, kita bisa menggunakan nama lebih pendek `planet`, tapi itu tak akan jelas planet apa. Bagus jika lebih panjang dan deskriptif. Minimal hingga variabel isNotTooLong.\n\n## Nama pengunjung saat ini\n\n```js\nlet currentUserName = \"John\";\n```\n\nLagi, kita bisa memperpendek jadi `userName` jika kita tahu pasti user saat ini.\n\nEditor modern dan autocomplete membuat nama variabel panjang mudah ditulis. Jangan pelit terhadap mereka. Nama yang mengandung 3 kata di dalamnya itu bagus.\n\nDan jika editormu tidak punya autocompletion yang layak, carilah [editor yang baru](/code-editors).\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/2-declare-variables/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Memberikan nama yang tepat\n\n1. Buat variabel dengan nama planet kita. Bagaimana kamu akan menamainya?\n2. Buat variabel untuk menyimpan nama pengunjung saat ini ke website. Bagaimana kamu menamainya?\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/3-uppercast-constant/solution.md",
    "content": "Kita umumnya menggunakan case yang layak untuk konstan yang  \"dihard-code\". Atau, dengan kata lain, ketika nilainya diketahui sebelum eksekusi dan langsung ditulis ke dalam kode.\n\nDi dalam kode, `birthday` memang seperti itu. Jadi kita bisa menggunakan kapital besar untuknya.\n\nSebaliknya, `age` dievaluasi saat run-time. Hari ini kita punya satu umur, setahun kemudian kita akan punya umur yang berbeda. Ia termasuk konstan yang takkan berubah melalui eksekusi kode. Tapi ia agak \"sedikit bukan konstan\" ketimbang `birthday`: ia dihitung, jadi sebaiknya kita menggunakan huruf kecil untuk itu.\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/3-uppercast-constant/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Const huruf kapital?\n\nCek kode berikut:\n\n```js\nconst birthday = '18.04.1982';\n\nconst age = someCode(birthday);\n```\n\nDi sini kita punya konstan tanggal `birthday` dan `age` dikalkulasi dari `birthday` dengan batuan beberapa kode (tidak tersedia yang pendek-pendek, dan karena detail tidak masalah di sini).\n\nApakah tepat menggunakan huruf kapital untuk `birthday`? Untuk `age`? Atau bahkan untuk keduanya?\n\n```js\nconst BIRTHDAY = '18.04.1982'; // buat huruf kapital?\n\nconst AGE = someCode(BIRTHDAY); // buat huruf kapital?\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/04-variables/article.md",
    "content": "# Variabel\n\nSeringnya, aplikasi JavaScript butuh kerja dengan informasi. Di sini ada dua contoh:\n1. Toko online -- informasinya mungkin berisi barang-barang yang dijual dan kereta belanja.\n2. Aplikasi chat -- informasinya mungkin berisi user, pesan, dan banyak lagi.\n\nVariabel digunakan untuk menyimpan informasi ini.\n\n## Variabel\n\n[Variabel](https://en.wikipedia.org/wiki/Variable_(computer_science)) ialah \"simpanan bernama\" untuk data. Kita bisa memakainya untuk menyimpan barang, pengunjung, dan data lain.\n\nUntuk membuat variabel di JavaScript, gunakan katakunci `let`.\n\nPernyataan di bawah membuat (dengan kata lain: *declares* or *defines*) variabel dengan nama \"message\":\n\n```js\nlet message;\n```\n\nKini, kita bisa menaruh beberapa data ke dalamnya dengan menggunakan operator penetapan `=`:\n\n```js\nlet message;\n\n*!*\nmessage = 'Hello'; // simpan string\n*/!*\n```\n\nString ini kini disimpan ke area memori yang terasosiasi dengan variabel. Kita bisa mengakses itu menggunakan nama variabel:\n\n```js run\nlet message;\nmessage = 'Hello!';\n\n*!*\nalert(message); // tampilkan isi variabel\n*/!*\n```\n\nSupaya ringkas, kita bisa mengkombinasi deklarasi variabel dan penetapan ke baris tunggal:\n\n```js run\nlet message = 'Hello!'; // definisikan variabel dan tetapkan nilai\n\nalert(message); // Hello!\n```\n\nKita juga bisa mendeklarasi variabel ganda dalam satu baris:\n\n```js no-beautify\nlet user = 'John', age = 25, message = 'Hello';\n```\n\nKelihatannya pendek, tapi itu tidak disarankan. Demi kemudahan dibaca, tolong gunakan bari tunggal per variabel.\n\nVarian multibaris agak panjang, tapi lebih mudah dibaca:\n\n```js\nlet user = 'John';\nlet age = 25;\nlet message = 'Hello';\n```\n\nBeberapa orang juga mendefinisi variabel ganda dalam gaya multibaris ini:\n```js no-beautify\nlet user = 'John',\n  age = 25,\n  message = 'Hello';\n```\n\n...Atau bahkan dalam gaya \"comma-first\":\n\n```js no-beautify\nlet user = 'John'\n  , age = 25\n  , message = 'Hello';\n```\n\nSecara teknis, semua varian ini melakukan hal yang sama. Jadi, cuma masaleh selera personal dan estetika saja.\n\n\n````smart header=\"`var` ketimbang `let`\"\nDi script jadul, kamu mungkin juga menemukan katakunci lain: `var` ketimbang `let`:\n\n```js\n*!*var*/!* message = 'Hello';\n```\n\nKatakunci `var` *hampir* sama dengan `let`. Gunanya untuk mendeklarasi variabel, tapi caranya agak sedikit beda, \"jadul\".\n\nAda perbedaan halus antara `let` dan `var`, tapi itu tak masalah buat kita sekarang ini. Kita akan mengcover mereka lebih detil di bab <info:var>.\n````\n\n## Analogy kehidupan nyata\n\nKita bisa dengan mudah memahami konsep \"variabel\" jika kita membayangkannya sebagai \"box\" untuk data, dengan stiker nama yang unik.\n\nMisalnya, variabel `message` bisa dibayangkan sebagai box berlabel `\"message\"` dengan nilai `\"Hello!\"` di dalamnya:\n\n![](variable.svg)\n\nKita bisa menaruh nilai apapun di dalam box.\n\nKita juga bisa mengubahnya sebanyak yang kita mau:\n```js run\nlet message;\n\nmessage = 'Hello!';\n\nmessage = 'World!'; // value changed\n\nalert(message);\n```\n\nKetika nilainya berubah, data lama dihapus dari variabel:\n\n![](variable-change.svg)\n\nKita juga bisa mendeklarasi dua variabel dan mengkopi data dari satu ke yang lainnya.\n\n```js run\nlet hello = 'Hello world!';\n\nlet message;\n\n*!*\n// mengkopi 'Hello world' dari hello ke message\nmessage = hello;\n*/!*\n\n// sekarang dua variabel punya data yang sama\nalert(hello); // Hello world!\nalert(message); // Hello world!\n```\n\n````warn header=\"Mendeklarasikan variabel dua kali akan menciptakan error\"\nSebuah variabel seharusnya di deklarasikan hanya sekali.\n\nMendeklarasikan variabel berkali kali akan menciptakan error:\n\n```js run\nlet message = \"This\";\n\n// repeated 'let' leads to an error\n// deklarasi 'let' berulang akan mengembalikan sebuah error\nlet message = \"That\"; // SyntaxError: 'message' has already been declared\n```\nJadi, kita harus mendeklarasikan variabel sekali dan menggunakan variabel tersebut tanpa menggunakan 'let'.\n````\n\n```smart header=\"Bahasa fungsional\"\nIni sangat menarik untuk diperhatikan bahwa ada bahasa pemrograman [fungsional](https://en.wikipedia.org/wiki/Functional_programming), seperti [Scala](http://www.scala-lang.org/) atau [Erlang](http://www.erlang.org/) yang melarang merubah nilai dari variabel.\n\nDi dalam bahasa macam ini, sekali nilai disimpan \"dalam box\", ia akan di sana selamanya. Jika kita harus menyimpan sesuatu yang lain, bahasa tersebut memaksa kita membuat box baru (mendeklarasi variabel baru). Kita tak bisa menggunakan ulang yang lama.\n\nMeski kelihatan sedikit aneh saat pandangan pertama, bahasa-bahasa ini ternyata mumpuni untuk pengembangan yang serius. Lebih dari itu, ada area seperti komputasi paralel di mana keterbatasan ini memberikan keuntungan tertentu. Disarankan mempelajari bahasa macam ini (meski jika kamu tak berencana menggunakannya segera) untuk meningkatkan wawasan.\n```\n\n## Penamaan variabel [#variable-naming]\n\nAda dua keterbatasan pada nama variabel di JavaScript:\n\n1. Nama hanya boleh mengandung huruf, digit, atau simbol seperti `$` and `_`.\n2. Karakter pertama tidak boleh digit.\n\nContoh nama valid:\n\n```js\nlet userName;\nlet test123;\n```\n\nKetika namanya mengandung kata ganda, [camelCase](https://en.wikipedia.org/wiki/CamelCase) umum digunakan: kata demi kata digabung, setiap kata kecuali yang pertama dimulai dengan huruf kapital: `myVeryLongName`.\n\nYang menarik ialah -- tanda dollar `'$'` dan underscore `'_'` juga bisa digunakan dalam nama. Mereka simbol reguler, hanya seperti huruf, tanpa makna yang spesial.\n\nNama-nama ini valid:\n\n```js run untrusted\nlet $ = 1; // declared a variable with the name \"$\"\nlet _ = 2; // and now a variable with the name \"_\"\n\nalert($ + _); // 3\n```\n\nContoh nama variabel yang tidak valid:\n\n```js no-beautify\nlet 1a; // cannot start with a digit\n\nlet my-name; // hyphens '-' aren't allowed in the name\n```\n\n```smart header=\"Case berpengaruh\"\nVariabel dengan nama `apple` dan `AppLE` adalah dua variabel yang berbeda.\n```\n\n````smart header=\"Huruf non-Latin diperbolehkan, namun tak direkomendasikan\"\nBoleh menggunakan bahasa apapun, termasuk huruf cyrillic atau bahkan hieroglyphs, seperti ini:\n\n```js\nlet имя = '...';\nlet 我 = '...';\n```\n\nSecara teknis, tak ada error di sini, nama-nama begitu boleh digunakan, tapi ada tradisi internasional untuk menggunakan bahasa Inggris dalam nama variabel. Meski jika kita menulis script kecil, kemungkinan variabelnya akan digunakan terus-menerus. Dan juga orang-orang dari negara lain mungkin harus membaca beberapa kali.\n````\n\n````warn header=\"Nama-nama yang dikecualikan\"\nAda [daftar kata yang dikecualikan](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#Keywords), yang tidak bisa digunakan sebagai nama variabel karena mereka digunakan oleh bahasa Javascript sendiri.\n\nMisalnya: `let`, `class`, `return`, dan `function` dikecualikan.\n\nKode di bawah menghasilkan galat syntax:\n\n```js run no-beautify\nlet let = 5; // tak bisa menamai variable \"let\", galat!\nlet return = 5; // juga tak bisa menamainya \"return\", galat!\n```\n````\n\n````warn header=\"Assignment tanpa `use strict`\"\n\nNormalnya, kita harus mendefinisi variabel sebelum memakainya. Tapi dulu, secara teknis boleh membuat variabel hanya dengan penetapan nilai tanpa menggunakan `let`. Ini masih berjalan jika kita tak menaruh `use strict` di script kita untuk mengelola kompatibilitas dengan script jadul.\n\n```js run no-strict\n// note: tak ada \"use strict\" di contoh ini\n\nnum = 5; // variabel \"num\" dibuat jika ia tak ada\n\nalert(num); // 5\n```\n\nIni kebiasaan buruk dan akan mengakibatkan galat dalam mode ketat:\n\n```js\n\"use strict\";\n\n*!*\nnum = 5; // galat: num tak terdefinisi\n*/!*\n```\n````\n\n## Konstan\n\nUntuk mendeklarasi variabel konstan (tak berubah), gunakan `const` ketimbang `let`:\n\n```js\nconst myBirthday = '18.04.1982';\n```\n\nVariabel yang dideklarasi menggunakan `const` disebut \"konstan\". Mereka tak bisa diubah. Jika kamu mencoba mengubahnya maka ia menghasilkan galat:\n\n```js run\nconst myBirthday = '18.04.1982';\n\nmyBirthday = '01.01.2001'; // error, tak bisa menetapkan-ulang konstan!\n```\n\nKetika programmer yakin bahwa variabel tak akan berubah, mereka bisa mendeklarasikan `const` untuk menjamin hal itu dan memberitahu semua orang.\n\n\n### Konstan huruf-besar\n\nAda kebiasaan umum untuk menggunakan konstan sebagai alias untuk nilai yang sulit dihafal yang akan diketahui sebelum dieksekusi.\n\nKonstan macam ini dinamai dengan huruf kapital dan underscore.\n\nMisalnya, mari kita buat konstan untuk warna dalam sesuatu yang disebut format \"web\" (hexadecimal):\n\n```js run\nconst COLOR_RED = \"#F00\";\nconst COLOR_GREEN = \"#0F0\";\nconst COLOR_BLUE = \"#00F\";\nconst COLOR_ORANGE = \"#FF7F00\";\n\n// ...ketika kita harus memilih warna\nlet color = COLOR_ORANGE;\nalert(color); // #FF7F00\n```\n\nKeuntungan:\n\n- `COLOR_ORANGE` lebih mudah diingat ketimbang `\"#FF7F00\"`.\n- Lebih rentan salah penulisan `\"#FF7F00\"` ketimbang `COLOR_ORANGE`.\n- Ketika membaca code, `COLOR_ORANGE` lebih berarti daripada `#FF7F00`.\n\nKapan kita sebaiknya menggunakan kapital untuk konstan dan kapan itu dinamai dengan normal? Ayo kita perjelas.\n\nMenjadi \"konstan\" hanya berarti jika nilai variable tak pernah berubah. Tapi ada konstan yang diketahui sebelum eksekusi (seperti nilai hexadecimal untuk merah) dan ada konstan yang *dikalkulasi* dalam run-time, selama eksekusi, tapi tak berubah setelah penetapan inisial mereka.\n\nMisalnya:\n```js\nconst pageLoadTime = /* waktu yang dibutuhkan laman web untuk meload */;\n```\n\nNilai `pageLoadTime` tidak diketahui sebelum laman diload, jadi itu dinamai dengan normal. Tapi ia masih konstan karena ia tak berubah setelah penetapan.\n\nDengan kata lain, konstan berhuruf kapital hanya digunakan sebagai alias untuk nilai yang \"dihard-code\".  \n\n## Namai dengan benar\n\nBerbicara tentang variabel, ada satu hal yang sangat penting.\n\nNama variabel sebaiknya punya arti yang bersih dan jelas, yang menjelaskan data yang ia simpan.\n\nPenamaan variabel adalah salah satu keahlian yang penting dan rumit dalam pemrograman. Pandangan sekilas pada nama variabel bisa menyingkap kode yang ditulis oleh pengembang pemula versus pengembang berpengalaman.\n\nDi proyek nyata, kebanyakan waktu dihabiskan untuk modifikasi dan mengextend code base ketimbang menulis sesuatu yang benar-benar  baru dari awal. Ketika kita kembali ke beberapa kode setelah melakukan sesuatu yang lain untuk sementara, akan lebih mudah menemukan informasi yang labelnya tepat. Atau, dengan kata lain, ketika variabel punya nama yang baik.\n\nTolong renungkan tentang nama yang baik untuk variabel sebelum mendeklarasinya. Itu baik untukmu.\n\nBeberapa aturan yang baik untuk ditiru:\n\n- Gunakan nama yang manusiawi seperti `userName` atau `shoppingCart`.\n- Jauhi singkatan atau nama pendek seperti `a`, `b`, `c`, kecuali jika kamu benar-benar tau apa yang kamu lakukan.\n- Buat nama sedeskriptif dan sejelas mungkin. Contoh nama yang jelek ialah `data` dan `value`. Nama semacam ini tidak punya makna dan hanya OK untuk menggunakannya jika data atau nilai variabel yang mengacu kontex kodenya luar biasa jelas.\n- Sepakat dalam hal-hal yang ada di dalam timmu dan pikiranmu. Jika pengunjung situs disebut \"user\" maka kita sebaiknya menamai variabel terkait `currentUser` atau `newUser` ketimbang `currentVisitor` atau `newManInTown`.\n\nKedengaran simpel kan? Jelas saja, tapi membuat nama variabel descriptif dan jelas pada praktiknya tidak mudah. Coba lakukan.\n\n```smart header=\"Daur ulang atau buat baru?\"\nDan catatan terakhirnya. Ada juga programmer malas yang, ketimbang mendeklarasi variabel baru, cenderung menggunakan variabel yang sudah ada.\n\nHasilnya, variabel mereka seperti box yang orang-orang lempar tanpa menganti stikernya. Apa yang ada di dalam boxnya? Siapa yang tahu? Kita harus mengeceknya dengan seksama.\n\nProgrammer macam ini pelit terhadap satu deklarasi variabel namun boros sepuluh kali saat debugging.\n\nVariabel extra itu baik, tidak jahat.\n\nJavaScript minifier dan peramban modern mengoptimisasi kode dengan cukup baik, sehingga ia tak akan mengakibatkan isu performa. Menggunakan variabel yang berbeda untuk nilai yang berbeda bahkan bisa membantu engine mengoptimisasi kodemu.\n```\n\n## Kesimpulan\n\nKita bisa mendeklarasi variabel untuk menyimpan data menggunakan katakunci `var`, `let`, atau `const`.\n\n- `let` -- adalah deklarasi variabel modern.\n- `var` -- adalah deklarasi variabel jadul. Normalnya kita tak menggunakannya sama sekali, tapi kita akan mengcover perbedaan halus dari `let` di bab <info:var>, hanya jika kamu membutuhkannya.\n- `const` -- seperti `let`, tapi nilai variabelnya tak bisa diubah.\n\nVariabel sebaiknya diberi nama yang memudahkan kita untuk memahami apa isinya.\n"
  },
  {
    "path": "1-js/02-first-steps/05-types/1-string-quotes/solution.md",
    "content": "\nBacktick mengembed expresi di dalam `${...}` ke dalam string.\n\n```js run\nlet name = \"Ilya\";\n\n// expresinya ialah angka 1\nalert( `hello ${1}` ); // hello 1\n\n// expresinya ialah nama string\nalert( `hello ${\"name\"}` ); // hello name\n\n// expresinya ialah variabel, embed dia\nalert( `hello ${name}` ); // hello Ilya\n```\n"
  },
  {
    "path": "1-js/02-first-steps/05-types/1-string-quotes/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Petik string\n\nApa output dari script ini?\n\n```js\nlet name = \"Ilya\";\n\nalert( `hello ${1}` ); // ?\n\nalert( `hello ${\"name\"}` ); // ?\n\nalert( `hello ${name}` ); // ?\n```"
  },
  {
    "path": "1-js/02-first-steps/05-types/article.md",
    "content": "# Tipe data\n\nDalam Javascript terdapat beberapa tipe data yang dapat digunakan, sebuah *string* atau sebuah *number*\n\nTerdapat delapan tipe data dasar dalam Javascript. Disini, kita akan mempelajarinya dan di bagian selanjutnya kita akan mempelajarinya secara detail.\n\nKita bisa menggunakan tipe apa saja didalam sebuah variabel. Contoh, Untuk sesaat sebuah variabel bisa saja memiliki tipe data *string* dan selanjutnya variabel tersebut menyimpan *number*:\n\n```js\n// Tidak ada error\nlet message = \"hello\";\nmessage = 123456;\n```\n\nBahasa pemrograman yang mendukung hal semacam ini, seperti Javascript, disebut dengan \"dynamically typed\", berarti terdapat tipe data, akan tetapi variabel tidak terikat pada tipe data apapun.\n\n## Number\n\n```js\nlet n = 123;\nn = 12.345;\n```\n\nTipe *number* merepresentasikan angka baik integer maupun floating point.\n\nAda banyak operasi untuk angka, misal perkalian `*`, pembagian `/`, penambahan `+`, pengurangan `-`, dan lainnya.\n\nSelain angka reguler, ada yang disebut \"nilai numerik spesial\" yang juga bagian dari tipe data ini: `Infinity`, `-Infinity` dan `NaN`.\n\n- `Infinity` mewakili [Infinity](https://en.wikipedia.org/wiki/Infinity) matematis ∞. Ia merupakan nilai spesial yang lebih besar dari angka apapun.\n\n    Kita bisa mendapatkannya sebagai hasil dari pembagian oleh nol:\n\n    ```js run\n    alert( 1 / 0 ); // Infinity\n    ```\n\n    Atau langsung referensikan saja dia:\n\n    ```js run\n    alert( Infinity ); // Infinity\n    ```\n- `NaN` mewakili error komputasional. Ia merupakan hasil operasi matematis yang salah atau tak-terdefinisi, misalnya:\n\n    ```js run\n    alert( \"not a number\" / 2 ); // NaN, pembagian macam ini keliru\n    ```\n\n    `NaN` itu lengket. Operasi lanjutan apapun pada `NaN` menghasilkan `NaN`:\n\n    ```js run\n    alert( \"not a number\" / 2 + 5 ); // NaN\n    ```\n\n    Jadi, jika ada `NaN` di manapun di expresi matematika, ia mempropagasi hasil keseluruhan.\n\n```smart header=\"Operasi matematika itu aman\"\nMelakukan matematika itu \"aman\" dalam JavaScript. Kita bisa melakukan apapun: pembagian dengan nol, memperlakukan string non-numerik sebagai angka, dll.\n\nScript ini takkan pernah stop dengan fatal error (\"die\"). Paling buruk, kita akan mendapat `NaN` sebagai hasilnya.\n```\n\nNilai numerik spesial formalnya merupakan bagian dari tipe \"number\". Tentu saja mereka bukan angka dalam pandangan umum dari kata ini.\n\nKita akan melihat lebih tentang cara bekerja dengan angka di bab <info:number>.\n\n## BigInt [#bigint-type]\n\nDidalam Javascript, tipe data \"number\" tidak bisa mengandung nilai lebih dari <code>(2<sup>53</sup>-1)</code> (sama dengan `9007199254740991`) atau kurang dari <code>-(2<sup>53</sup>-1)</code>. Itu adalah batasan teknik yang dibuat.\n\nUntuk kebanyakan kebutuhan sebenarnya sudah cukup, dan terkadang kita membutuhkan nilai yang lebih besar, contohnya untuk kriptografy atau perhitungan waktu microsecond.\n\nTipe data `BigInt` lalu ditambahkan kedalam Javascript untuk menampilkan nilai *integer* yang sangat panjang.\n\nTipe data `BigInt` dibuat dengan menambahkan `n` diakhir dari nilai sebuah *integer*.\n\n```js\n// arti dari \"n\" pada akhir menandakan bahwa contoh dibawah adalah sebuah `BigInt`\nconst bigInt = 1234567890123456789012345678901234567890n;\n```\n\nSebenarnya `BigInt` jarang dibutuhkan, kita tidak akan mempelajarinya disini, tetapi akan dipisahkan didalam bagian <info:bigint>. Baca saja saat kamu membutuhkan nilai *integer* yang sangat panjang.\n\n\n```smart header=\"Masalah Kompabilitas\"\nSekarang `BigInt` sudah didukung oleh Firefox/Chrome/Edge, tapi tidak didalam Safari/Internet Explorer.\n\n\nYou can check [*MDN* BigInt compatibility table](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#Browser_compatibility) to know which versions of a browser are supported.\n\n## String\n\nString di JavaScript harus dikelilingi petik.\n\n```js\nlet str = \"Hello\";\nlet str2 = 'Single quotes are ok too';\nlet phrase = `can embed another ${str}`;\n```\n\nDi JavaScript, ada 3 tipe petik.\n\n1. Petik ganda: `\"Hello\"`.\n2. Petik tunggal: `'Hello'`.\n3. Backtick: <code>&#96;Hello&#96;</code>.\n\nPetik tunggal dan ganda merupakan petik \"simpel\". Tak ada perbedaan antara mereka di JavaScript.\n\nBacktick merupakan petik \"fungsional lanjutan\". Mereka memungkinkan kita mengembed variabel dan expresi ke dalam string dengan membungkus mereka dalam `${…}`, misalnya:\n\n```js run\nlet name = \"John\";\n\n// mengembed satu variabel\nalert( `Hello, *!*${name}*/!*!` ); // Hello, John!\n\n// mengembed expresi\nalert( `the result is *!*${1 + 2}*/!*` ); // hasilnya 3\n```\n\nExpresi di dalam `${…}` dievaluasi dan hasilnya menjadi bagian dari string. Kita bisa menaruh apapun di sana: variabel seperti `name` atau expresi aritmatika seperti `1 + 2` atau sesuatu yang lebih rumit.\n\nTolong diingat bahwa ini hanya bisa dilakukan dalam backtick. Petik lain tidak punya fungsionalitas pengembedan!\n```js run\nalert( \"the result is ${1 + 2}\" ); // hasilnya ${1 + 2} (petik ganda tak akan berpengaruh)\n```\n\nKita akan mengcover string lebih dalam di bab <info:string>.\n\n```smart header=\"Tidak ada tipe *character*.\"\nDalam beberapa bahasa, ada tipe \"character\" spesial untuk karakter tunggal. Misalnya, di bahasa C dan di Java adalah `char`.\n\nDi JavaScript, tak ada tipe semacam itu. Cuma ada satu tipe: `string`. String bisa berisi satu karakter atau lebih.\n```\n\n## Boolean (tipe logika)\n\nTipe boolean cuma punya dua nilai: `true` dan `false`.\n\nTipe ini umumnya digunakan untuk menyimpan niai ya/tidak: `true` artinya \"ya, betul\", dan `false` artinya \"tidak, salah\".\n\nMisalnya:\n\n```js\nlet nameFieldChecked = true; // ya, field nama dicek\nlet ageFieldChecked = false; // tidak, field usia tak dicek\n```\n\nNilai boolean juga datang dari perbandingan:\n\n```js run\nlet isGreater = 4 > 1;\n\nalert( isGreater ); // benar (hasil perbandingan yaitu \"ya\")\n```\n\nKita akan mengcover boolean lebih dalam di bab <info:logical-operators>.\n\n## Nilai \"null\"\n\nNilai `null` spesial bukan bagian dari tipe apapun yang dijelaskan di atas.\n\nIa membentuk tipe terpisah miliknya sendiri yang cuma berisi nilai `null`:\n\n```js\nlet age = null;\n```\n\nDi JavaScript, `null` tidak \"mereferensi ke objek yang tak ada\" atau \"null pointer\" seperti beberapa bahasa lain.\n\nIa cuma nilai spesial yang mewakili \"hampa\", \"kosong\" atau \"nilai tak-diketahui\".\n\nKode diatas mengatakan bahwa `age` tidak diketahui.\n\n## Nilai \"undefined\"\n\nNilai spesial `undefined` juga berbeda lagi. Ia punya tipe miliknya sendiri, sama seperti `null`.\n\nArti `undefined` ialah \"nilai yang tak ditetapkan\".\n\nJika variabel dideklarasi, namun tak ditetapkan, maka nilainya `undefined`:\n\n```js run\nlet age;\n\nalert(age); // menampilkan \"undefined\"\n```\n\nSecara teknis, kita bisa secara jelas menetapkan `undefined` kedalam sebuah variabel:\n\n```js run\nlet age = 100;\n\n// mengubah nilai menjadi undefined\nage = undefined;\n\nalert(age); // \"undefined\"\n```\n\n...Tapi kita tidak menyarankan itu. Normalnya, kita gunakan `null` untuk menetapkan nilai \"kosong\" atau \"tak-diketahui\" ke variabel, dan kita gunakan `undefined` untuk pengecekan seperti melihat apakah nilai dari variabel telah ditetapkan.\n\n## Objek dan Simbol\n\nTipe `object` itu special.\n\nSeluruh tipe data lainnya disebut \"primitive\" karena hanya bisa mengandung satu buah nilai (entah itu sebuah string ataupun number atau lainnya). Sebaliknnya, object digunakan untuk menyimpan koleksi dari data dan entitas lainnya.\n\nObjek itu penting, objek membutuhkan perlakuan yang spesial. Kita akan pelajari objek lebih lanjut di bagian <info:object>, setelah kita pelajari lebih lanjut tentang tipe data primitif.\n\nTipe `symbol` digunakan untuk menciptakan identifier unik untuk sebuah objek. Untuk kelengkapan kita akan menyebutkannya disini, tetapi akan ditunda hingga kita tahu tentang objek\n\n## Operator typeof [#type-typeof]\n\nOperator `typeof` mengembalikan tipe argumen. Berguna ketika kita ingin memproses nilai dari tipe berbeda secara berbeda atau cuma ingin mengecek sekilas.\n\nIa mendukung dua bentuk syntax:\n\n1. Sebagai operator: `typeof x`.\n2. Sebagai fungsi: `typeof(x)`.\n\nDengan kata lain, ia berjalan dengan atau tanpa kurung. Hasilnya sama saja.\n\nPanggilan ke `typeof x` mengembalikan string dengan nama tipenya:\n\n```js\ntypeof undefined // \"undefined\"\n\ntypeof 0 // \"number\"\n\ntypeof 10n // \"bigint\"\n\ntypeof true // \"boolean\"\n\ntypeof \"foo\" // \"string\"\n\ntypeof Symbol(\"id\") // \"symbol\"\n\n*!*\ntypeof Math // \"object\"  (1)\n*/!*\n\n*!*\ntypeof null // \"object\"  (2)\n*/!*\n\n*!*\ntypeof alert // \"function\"  (3)\n*/!*\n```\n\nTiga baris terakhir mungkin butuh penjelasan tambahan:\n\n1. `Math` ialah objek built-in yang menyediakan operasi matematik. Kita akan belajar itu di bab <info:number>. Di sini, ia cuma sekedar contoh dari objek.\n2. Hasil `typeof null` yaitu `\"object\"`. Itu salah. Ia merupakan error yang terkenal resmi dalam `typeof`, yang dijaga untuk kompatibilitas. Tentu saja, `null` bukanlah objek. Ia merupakan nilai spesial dengan tipe terpisah miliknya sendiri. Jadi, lagi, ini merupakan error dalam bahasa.\n3. Hasil dari `typeof alert` yaitu `\"function\"`, karena `alert` merupakan fungsi. Kita akan belajar fungsi di bab berikutnya di mana kita juga akan melihat bahwa tak ada tipe \"fungsi\" spesial di JavaScript. Fungsi merupakan bagian dari tipe objek. Tapi `typeof` memperlakukan mereka secara berbeda, yang mengembalikan `\"fungsi\"`. Itu tak sepenuhnya benar, tapi sangat nyaman pada praktiknya.\n\n## Kesimpulan\n\nAda 7 tipe data dasar dalam JavaScript.\n\n- `number` untuk nomor dengan bentuk apapun: integer ataupun nilai yang memiliki nilai desimal, batas dari integer adalah ±2<sup>53</sup>.\n- `bigint` untuk nomor integer yang sangat panjang.\n- `string` untuk string. Sebuah string mungkin memiliki 0 atau lebih karakter, tidak ada tipe data untuk string yang memiliki panjang 1 karakter.\n- `boolean` untuk `true`/`false`.\n- `null` untuk nilai yang tidak diketahui -- sebuah tipe data mandiri yang memiliki satu nilai yaitu `null`.\n- `undefined` untuk nilai yang tidak ada atau tidak diberikan nilai -- sebuah tipe data mandiri yang memiliki satu nilai yaitu `null`.\n- `object` untuk struktur data yang lebih rumit.\n- `symbol` untuk identifier atau pengenal yang unik.\n\nOperator `typeof` memungkinkan kita melihat tipe mana yang disimpan dalam variable.\n\n- Dua form: `typeof x` atau `typeof(x)`.\n- Mengembalikan string dengan nama tipe, seperti `\"string\"`.\n- Untuk `null` mengembalikan `\"object\"` -- ada error dalam bahasa, yang sebenarnya bukan objek.\n\nDi bab berikutnya, kita akan fokus pada nilai primitive dan sekali kita familiar dengan mereka, kita akan lanjut ke objek.\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/solution.md",
    "content": "Kode JavaScript:\n\n```js demo run\nlet name = prompt(\"Siapakah nama Anda?\", \"\");\nalert(name);\n```\n\nLaman penuh:\n\n```html\n<!DOCTYPE html>\n<html>\n<body>\n\n  <script>\n    'use strict';\n\n    let name = prompt(\"Siapakah nama Anda?\", \"\");\n    alert(name);\n  </script>\n\n</body>\n</html>\n```\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/1-simple-page/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Laman simpel\n\nBuat laman web yang meminta nama dan menampilkannya.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/06-alert-prompt-confirm/article.md",
    "content": "# Interaksi: alert, prompt, confirm\n\nsebagaimana kita akan menggunakan peramban sebagai lingkungan percobaan kode, ayo kita lihat beberapa fungsi untuk berinteraksi dengan pengguna: `alert`, `prompt` dan `confirm`.\n\n## alert\n\nUntuk yang satu ini kita sudah pernah melihatnya. Ini akan menampilkan pesan dan menunggu pengguna untuk menekan tombol \"OK\".\n\nContoh:\n\n```js run\nalert(\"Hello\");\n```\n\nMini-window dengan pesan ini disebut *modal window*. Kata \"modal\" artinya pengunjung tak bisa berinteraksi dengan apapun di laman, menekan tombol lain, dll. hingga mereka selesai berurusan dengan window ini. Dalam hal ini -- hingga mereka menekan \"OK\".\n\n## prompt\n\nFungsi `prompt` menerima dua argumen:\n\n```js no-beautify\nresult = prompt(title, [default]);\n```\n\nIa menampilkan modal window dengan pesan teks, input field untuk pengunjung, dan tombol OK/CANCEL.\n\n`title`\n: Teks untuk ditampilkan ke pengunjung.\n\n`default`\n: Parameter kedua opsional, nilai inisial untuk input field.\n\n```smart header=\"Kurung siku didalam sintaks `[...]`\"\nKurung siku di sintaks `default` di kode sintaks di atas menandakan bahwa parameter itu bersifat opsional, tidak benar-benar dibutuhkan.\n```\n\nPengunjung halaman bisa mengetik sesuatu didalam kotak prompt dan menekan tombol OK. Lalu kita akan mendapatkan teksnya didalam `result`. Atau pengunjung halaman bisa membatalkan kotak promp dengan menekan *Cancel* atau menekan `key:Esc` pada *keyboard*, lalu kita akan mendapatkan `null` sebagai `result`.\n\nPanggilan ke `prompt` mengembalikan teks dari input field atau `null` jika input dibatalkan.\n\nMisalnya:\n\n```js run\nlet age = prompt('Berapakah umut anda?', 100);\n\nalert(`Umur Anda ${age} tahun`); // Umur Anda 100 tahun!\n```\n\n```warn header=\"In IE: selalu isikan nilai `default`\"\nParameter kedua ini opsional, tapi jika kita tidak menyuplai, Internet Explorer akan menyisipkan teks `\"undefined\"` ke dalam prompt.\n\nJalan kode ini di Internet Explorer untuk melihat:\n\n```js run\nlet test = prompt(\"Test\");\n```\n\nJadi, supaya prompt terlihat bagus di IE, sebaiknya sediakan argumen kedua:\n\n```js run\nlet test = prompt(\"Test\", ''); // <-- for IE\n```\n\n## confirm\n\nSyntaxnya:\n\n```js\nresult = confirm(question);\n```\n\nFungsi `confirm` menampilkan modal window dengan `pertanyaan` dan dua tombol: OK dan Cancel.\n\nHasilnya `true` jika OK ditekan dan `false` jika tidak.\n\nMisalnya:\n\n```js run\nlet isBoss = confirm(\"Are you the boss?\");\n\nalert( isBoss ); // true jika OK ditekan\n```\n\n## Kesimpulan\n\nKita membahas 3 fungsi spesifik peramban untuk berinteraksi dengan pengunjung:\n\n`alert`\n: menampilkan pesan.\n\n`prompt`\n: menampilkan pesan yang minta input teks pengguna. Ia mengembalikan teks atau, jika Cancel atau `key:Esc` diklik, `null`.\n\n`confirm`\n: menampilkan pesan dan menunggu pengguna menekan \"OK\" atau \"Cancel\". Ia mengembalikan `true` untuk OK dan `false` untuk Cancel/`key:Esc`.\n\nSemua metode ini ialah modal: mereka menyela exekusi script dan tak membolehkan pengunjung berinteraksi dengan apapun di laman hingga window ditutup.\n\nAda dua batasan yang dibagikan semua metode di atas:\n\n1. Lokasi tepat modal window ditentukan oleh peramban. Biasanya, di tengah.\n2. Tampilan tepat window juga tergantung peramban. Kita tak bisa  modifikasi ini.\n\nItulah harga untuk kesederhanaan. Ada banyak cara menampilkan window lebih manis dan kaya akan interaksi dengan pengguna, tapi jika \"bells and whistles\" tak jadi masalah, metode ini baik-baik saja.\n"
  },
  {
    "path": "1-js/02-first-steps/07-type-conversions/article.md",
    "content": "# Konversi Tipe\n\nSeringkali, operator dan fungsi otomatis mengkonversi nilai yang diberikan ke mereka ke tipe yang sesuai. \n\nMisalnya, `alert` otomatis mengkonversi nilai apapun ke string untuk menampilkannya. Operasi mathematika mengkonversi nilai ke angka.\n\nAda juga kasus di mana kita harus explisit mengkonversi nilai ke tipe yang diharapkan.\n\n```smart header=\"Belum bicara objek dulu\"\nDi bab ini, kita takkan mengcover objek. Daripada itu, kita akan belajar primitives dulu.\n\nLalu, setelah kita belajar tentang objek, kita akan lihat cara konversi objek bekerja di bab <info:object-toprimitive>.\n```\n\n## Konversi String\n\nKonversi string terjadi ketika kita butuh bentuk string dari nilai.\n\nMisalnya, `alert(value)` menampilkan nilai.\n\nKita juga bisa memanggil fungsi `String(value)` untuk mengkonversi nilai string:\n\n```js run\nlet value = true;\nalert(typeof value); // boolean\n\n*!*\nvalue = String(value); // sekarang nilainya string \"true\"\nalert(typeof value); // string\n*/!*\n```\n\nKonversi string kebanyakan jelas. `false` menjadi `\"false\"`, `null` menjadi `\"null\"`, dll.\n\n## Konversi Numerik\n\nKonversi numerik terjadi otomatis dalam fungsi dan expresi matematis.\n\nMisalnya, ketika pembagian `/` dilakukan ke non-angka:\n\n```js run\nalert( \"6\" / \"2\" ); // 3, string dikonversi ke angka\n```\n\nKita bisa gunakan fungsi `Number(value)` untuk explisit mengkonversi `value` ke angka:\n\n```js run\nlet str = \"123\";\nalert(typeof str); // string\n\nlet num = Number(str); // menjadi angka 123\n\nalert(typeof num); // angka\n```\n\nKonversi explisit biasanya dibutuhkan ketika kita membaca nilai dari sumber berbasis string seperti form teks namun mengharapkan angka untuk dienter.\n\nJika stringnya angka tak valid, hasilnya konversi macam ini ialah `NaN`. Misalnya:\n\n```js run\nlet age = Number(\"an arbitrary string instead of a number\");\n\nalert(age); // NaN, konversi gagal\n```\n\nAturan konversi numerik:\n\n| Value |  Becomes... |\n|-------|-------------|\n|`undefined`|`NaN`|\n|`null`|`0`|\n|<code>true&nbsp;dan&nbsp;false</code> | `1` and `0` |\n| `string` | Whitespaces dari awal dan akhir dibuang. Jika string sisanya kosong, hasilnya `0`. Sebaliknya, angkanya \"dibaca\" dari stringnya. Error memberikan `NaN`. |\n\nMisalnya:\n\n```js run\nalert( Number(\"   123   \") ); // 123\nalert( Number(\"123z\") );      // NaN (error membaca angka pada \"z\")\nalert( Number(true) );        // 1\nalert( Number(false) );       // 0\n```\n\nTolong diingat bahwa kelakuan `null` dan `undefined` berbeda di sini: `null` menjadi nol namun `undefined` menjadi `NaN`.\n\nHampir semua operasi matematik melakukan konversi semacam ini, yang akan kita lihat di bab berikutnya.\n\n## Konversi Boolean\n\nKonversi boolean ialah yang paling simpel.\n\nIni terjadi dalam operasi logika (nanti kita akan menemui tes kondisi dan hal mirp lainnya) tapi bisa juga berjalan explisit dengan panggilan ke `Boolean(value)`.\n\nAturan konversi:\n\n- Nilai yang secara intuitif \"kosong\", seperti `0`, string kosong, `null`, `undefined`, dan `NaN`, menjadi `false`.\n- Nilai lainnya menjadi `true`.\n\nMisalnya:\n\n```js run\nalert( Boolean(1) ); // true\nalert( Boolean(0) ); // false\n\nalert( Boolean(\"hello\") ); // true\nalert( Boolean(\"\") ); // false\n```\n\n````warn header=\"Please note: the string with zero `\\\"0\\\"` is `true`\"\nSome languages (namely PHP) treat `\"0\"` as `false`. But in JavaScript, a non-empty string is always `true`.\n\n```js run\nalert( Boolean(\"0\") ); // true\nalert( Boolean(\" \") ); // spaces, also true (any non-empty string is true)\n```\n````\n\n## Kesimpulan\n\nTiga tipe konversi yang paling digunakan ialah ke string, ke angka, dan ke boolean.\n\n**`Konversi String`** -- Terjadi ketika kita mengoutput sesuatu. Bisa berjalan dengan `String(value)`. Konversi ke string biasanya untuk nilai primitif.\n\n**`Konversi Numerik`** -- Terjadi di operasi matematika. Bisa berjalan dengan `Number(value)`.\n\nKonversinya mengikuti aturan ini:\n\n| Value |  Becomes... |\n|-------|-------------|\n|`undefined`|`NaN`|\n|`null`|`0`|\n|<code>true&nbsp;/&nbsp;false</code> | `1 / 0` |\n| `string` | Stringnya dibaca \"apa adanya\", whitespace dari kedua sisi diabaikan. String kosong menjadi `0`. Error memberikan `NaN`. |\n\n**`Konversi Boolean`** -- Terjadi di operasi logika. Bisa berjalan dengan `Boolean(value)`.\n\nIkuti aturan ini:\n\n| Value |  Becomes... |\n|-------|-------------|\n|`0`, `null`, `undefined`, `NaN`, `\"\"` |`false`|\n|nilai lain apapun| `true` |\n\n\nKebanyakan aturan ini mudah dipahami dan diingat. Pengecualian penting di mana orang biasanya membuat kesalahan yaitu:\n\n- `undefined` ialah `NaN` sebagai angka, buka `0`.\n- `\"0\"` dan string yang cuma-spasi seperti `\"   \"` ialah true sebagai boolean.\n\nObjek tidak dicover di sini. Kita akan kembali ke mereka nanti di bab <info:object-toprimitive> yang khusus exclusif untuk objeck setelah kita belajar hal-hal lebih dasar tentang JavaScript.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/1-increment-order/solution.md",
    "content": "\nJawabannya adalah:\n\n- `a = 2`\n- `b = 2`\n- `c = 2`\n- `d = 1`\n\n```js run no-beautify\nlet a = 1, b = 1;\n\nalert( ++a ); // 2, bentuk prefix mengembalikan nilainya\nalert( b++ ); // 1, bentuk postfix mengembalikan nilai lamanya\n\nalert( a ); // 2, diinkremen sekali\nalert( b ); // 2, diinkremen sekali\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/1-increment-order/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Bentuk postfix dan prefix\n\nBerapa nilai final dari semua variabel `a`, `b`, `c` dan `d` setelah kode berikut?\n\n```js\nlet a = 1, b = 1;\n\nlet c = ++a; // ?\nlet d = b++; // ?\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/2-assignment-result/solution.md",
    "content": "Jawabannya adalah:\n\n- `a = 4` (dikali 2)\n- `x = 5` (dikalkulasi sebagai 1 + 4)\n\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/2-assignment-result/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Hasil penetapan\n\nBerapa nilai dari `a` dan `x` setelah kode berikut?\n\n```js\nlet a = 2;\n\nlet x = 1 + (a *= 2);\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/3-primitive-conversions-questions/solution.md",
    "content": "\n```js no-beautify\n\"\" + 1 + 0 = \"10\" // (1)\n\"\" - 1 + 0 = -1 // (2)\ntrue + false = 1\n6 / \"3\" = 2\n\"2\" * \"3\" = 6\n4 + 5 + \"px\" = \"9px\"\n\"$\" + 4 + 5 = \"$45\"\n\"4\" - 2 = 2\n\"4px\" - 2 = NaN\n\"  -9  \" + 5 = \"  -9  5\" // (3)\n\"  -9  \" - 5 = -14 // (4)\nnull + 1 = 1 // (5)\nundefined + 1 = NaN // (6)\n\" \\t \\n\" - 2 = -2 // (7)\n```\n\n1. Penambahan dengan string `\"\" + 1` mengkonversi `1` ke string: `\"\" + 1 = \"1\"`, dan kita punya `\"1\" + 0`, aturan yang sama berlaku.\n2. Pengurangan `-` (seperti kebanyakan operasi matematika) cuma berjalan dengan angka, ia mengkonversi string kosong `\"\"` ke `0`.\n3. Penambahan dengan string mengappend angka `5` ke string.\n4. Pengurangan selalu mengkonversi ke angka, jadi ia membuat `\"  -9  \"` menjadi angka `-9` (mengabaikan spasi sekitarnya).\n5. `null` menjadi `0` setelah konversi numerik.\n6. `undefined` menjadi `NaN` setelah konversi numerik.\n7. Karakter spasi, ialah string yang depan dan belakangnya ditrim ketika string dikonversi ke angka. Berikut seluruh string berisi karakter spasi, seperti `\\t`, `\\n` dan spasi \"reguler\" di antaranya. Jadi, serupa dengan string kosong, ia menjadi `0`.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/3-primitive-conversions-questions/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Konversi tipe\n\nApa hasil dari expresi ini?\n\n```js no-beautify\n\"\" + 1 + 0\n\"\" - 1 + 0\ntrue + false\n6 / \"3\"\n\"2\" * \"3\"\n4 + 5 + \"px\"\n\"$\" + 4 + 5\n\"4\" - 2\n\"4px\" - 2\n\"  -9  \" + 5\n\"  -9  \" - 5\nnull + 1\nundefined + 1\n\" \\t \\n\" - 2\n```\n\nPikirkan dengan baik, tulis dan bandingkan dengan jawaban.\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/4-fix-prompt/solution.md",
    "content": "Alasannya adalah karena kotak *prompt* mengembalikan inputan dari user sebagai string.\n\nJadi nilai dari masing-masing variabel adalah `\"1\"` dan `\"2\"`.\n\n```js run\nlet a = \"1\"; // prompt(\"Angka pertama?\", 1);\nlet b = \"2\"; // prompt(\"Angka kedua?\", 2);\n\nalert(a + b); // 12\n```\n\nApa yang kita harus lakukan untuk mengubah string menjadi angka sebelum `+`, gunakan `Number()` atau menambahkannya dengan `+`.\n\nContoh, tepat sebelum `prompt`:\n\n```js run\nlet a = +prompt(\"Angka pertama?\", 1);\nlet b = +prompt(\"Angka kedua?\", 2);\n\nalert(a + b); // 3\n```\n\nAtau didalam `alert`:\n\n```js run\nlet a = prompt(\"Angka pertama?\", 1);\nlet b = prompt(\"Angka kedua?\", 2);\n\nalert(+a + +b); // 3\n```\n\nMenggunakan *unary* dan *binary* `+` dicontoh kode terakhir terlihat lucu, kan?\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/4-fix-prompt/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Benarkan penambahan\n\nIni adalah kode yang menanyakan pengguna untuk memasukan dua angka dan menampilkan jumlahnya.\n\nKodenya tidak berjalan dengan semestinya. Keluaran dari contoh kode dibawah adalah `12` (nilai dimasukan secara default didalam kotak prompt).\n\nKenapa? Benarkan. Hasilnya haruslah `3`.\n\n```js run\nlet a = prompt(\"Angka pertama?\", 1);\nlet b = prompt(\"Angka kedua?\", 2);\n\nalert(a + b); // 12\n```\n"
  },
  {
    "path": "1-js/02-first-steps/08-operators/article.md",
    "content": "# Operator dasar, maths\n\nKita tahu banyak operator dari sekolah. Mereka adalah penambahan `+`, perkalian `*`, pengurangan `-`, dll.\n\nDidalam bagian ini, kita akan memulai dengan menggunakan operator sederhana, lalu kita akan fokus pada aspek khusus Javascript, yang tidak dipelajari pada aritmatika di sekolah.\n\n## Istilah: \"unary\", \"binary\", \"operand\"\n\nSebelum kita lanjut, mari pahami dulu terminologi umum.\n\n- *Operand* -- untuk apa operator diaplikasikan. Misalnya, dalam perkalian `5 * 2` ada dua operand: operand kiri `5` dan operand kanan `2`. Kadang, orang memanggil ini \"argumen\" ketimbang \"operand\".\n- Operator disebut *unary* jika ia punya operand tunggal. Misalnya, negasi unary `-` membalikkan tanda dari angka:\n\n    ```js run\n    let x = 1;\n\n    *!*\n    x = -x;\n    */!*\n    alert( x ); // -1, negasi unary diaplikasikan\n    ```\n- Operator disebut *binary* jika ia punya dua operand. Minus yang sama juga berada dalam bentuk binary:\n\n    ```js run no-beautify\n    let x = 1, y = 3;\n    alert( y - x ); // 2, minus binary mengurangi nilai\n    ```\n\n    Formalnya, di contoh di atas kita punya dua operator berbeda yang berbagi simbol yang sama: operator negasi, operator unary yang membalik tanda, dan operator pengurangan, operator biner yang mengurangi angka satu dengan lainnya.\n\n## Maths\n\nOperasi matematika dibawah telah didukung didalam Javascript:\n\n- Penambahan `+`,\n- Pengurangan `-`,\n- Perkalian `*`,\n- Pembagian `/`,\n- Sisa Bagi `%`,\n- Eksponensial `**`.\n\nKeempat operasi pertama sudah cukup jelas, sementara `%` dan `**` membutuhkan lebih banyak kata-kata untuk dijelaskan.\n\n### Sisa bagi %\n\nOperator sisa bagi `%` sebagaimana kelihatannya, tidak berhubungan dengan persen.\n\nHasil dari `a % b` adalah [nilai sisa](https://en.wikipedia.org/wiki/Remainder) dai pembagian antara `a` oleh `b`.\n\nContoh\n\n```js run\nalert( 5 % 2 ); // 1, sisa dari pembagian antara 5 dibagi 2\nalert( 8 % 3 ); // 2, sisa dari pembagian antara 8 dibagi 3\n```\n\n### Eksponensial **\n\nOperator eksponensial `a ** b` mengkalikan `a` dengan nilai itu sendiri sebanyak `b` kali.\n\nDalam matematika sekolah, kita menuliskannya sebagai<sup>b</sup>.\n\nalert( 2 ** 2 ); // 2² = 4\nalert( 2 ** 3 ); // 2³ = 8\nalert( 2 ** 4 ); // 2⁴ = 16\n```\n\nSama seperti dalam matematika, operator eksponensial juga didefinisikan untuk bilangan non-bilangan bulat.\n\nMisalnya, akar kuadrat adalah eksponensial dengan :\n\n```js run\nalert( 4 ** (1/2) ); // 2 (pangkat 1/2 sama dengan akar kuadrat)\nalert( 8 ** (1/3) ); // 2 (pangkat 1/3 sama dengan akar kubik)\n```\n\n\n## Penambahan string dengan +\n\nAyo kita bertemu dengan fitur dari operator Javascript yang berada diatas aritmatika di sekolah.\n\nBiasanya, operator plus `+` menambah angka.\n\nTapi, jika binary `+` diaplikasikan ke string, ia menggabungkan (konkatenasi) mereka:\n\n```js\nlet s = \"my\" + \"string\";\nalert(s); // mystring\n```\n\nIngat bahwa jika salah satu operand berupa string, maka yang satunya dikonversi ke string juga.\n\nMisalnya:\n\n```js run\nalert( '1' + 2 ); // \"12\"\nalert( 2 + '1' ); // \"21\"\n```\n\nLihat, tak masalah operand manapun yang berupa string. Aturannya simpel: jika salah satu operand adalah string, maka yang satunya dikonversi ke string juga.\n\nNamun, ingat bahwa operasi berjalan dari kiri ke kanan. Jika ada dua angka diikuti string, angka akan ditambah sebelum dikonversi ke string:\n\nIni adalah contoh yang lebih rumit:\n\n```js run\nalert(2 + 2 + '1' ); // \"41\" dan bukan \"221\"\n```\n\nDisini, operator bekerja secara bergantian. Pertama `+` menambahkan dua angka, jadi akan menghasilkan `4`, lalu selanjutnya `+` menambahkan string `1` kedalamnya, jadi akan menjadi seperti `4 + '1' = 41`.\n\n```js run\nalert('1' + 2 + 2); // \"122\" dan bukan \"14\"\n```\ndisini, bilangan pertama adalah string, kompiler memperlakukan dua bilangan lainnya sebagai string juga. Bilangan `2` ditambahkan dengan `1`, jadi `'1' + 2 = \"12\"` dan `\"12\" + 2 = \"122\"`.\n\nOperator `+` hanyalah satu-satunya operator yang mendukung penggunaan string dengan cara semacan itu. Operator aritmatika lainnya hanya bekerja dengan angka dan selalu mengubah operannya menjadi angka.\n\nIni adalah contoh untuk pengurangan dan pembagian:\n\n```js run\nalert( 6 - '2' ); // 4, mengubah '2' menjadi angka\nalert( '6' / '2' ); // 3, mengubah keduanya menjadi angka\n```\n\n## Konversi angka, unary +\n\nPlus `+` ada dalam dua bentuk: bentuk binary yang kita gunakan di atas dan bentuk unary.\n\nPlus unary atau, dalam kata lain, operator plus `+` diaplikasikan ke nilai tunggal, tak berefek apapun ke angka. Tapi jika operand bukan angka, plus unary dikonversi ke dalam angka.\n\nMisalnya:\n\n```js run\n// Tak ada efek ke angka\nlet x = 1;\nalert( +x ); // 1\n\nlet y = -2;\nalert( +y ); // -2\n\n*!*\n// Mengkonversi non-angka\nalert( +true ); // 1\nalert( +\"\" );   // 0\n*/!*\n```\n\nSebenarnya ia melakukan hal yang sama seperti `Number(...)`, tapi lebih pendek.\n\nKebutuhan mengkonversi string ke angka sangat sering meningkat. Misalnya, jika kita memperoleh nilai dari kolom di form HTML, mereka biasanya string. Bagaimana jika kita ingin menjumlahkan mereka?\n\nPlus binary akan menambah mereka sebagai string:\n\n```js run\nlet apples = \"2\";\nlet oranges = \"3\";\n\nalert( apples + oranges ); // \"23\", plus binary mengkonkatenasi string\n```\n\nJika kita ingin memperlakukan mereka sebagai angka, kita harus mengkonversi, lalu menjumlahkan mereka:\n\n```js run\nlet apples = \"2\";\nlet oranges = \"3\";\n\n*!*\n// kedua nilai dikonversi ke angka sebelum plus binary\nalert( +apples + +oranges ); // 5\n*/!*\n\n// varian lebih panjang\n// alert( Number(apples) + Number(oranges) ); // 5\n```\n\nDari sisi pandang matematikawan, melimpahnya plus terlihat aneh. Tapi dari sisi pandang programmer, tak ada yang spesial: plus unary diaplikasikan dahulu, lalu mengkonversi string ke angka, dan lalu binary plus menjumlahkan mereka.\n\nKenapa plus unary diaplikasi ke nilai sebelum binarynya? Seperti yang kita lihat, itu karena *peresedensi lebih tinggi* mereka.\n\n## Presedensi operator\n\nJika expresi punya lebih dari satu operator, urutan eksekusi ditentukan oleh *presedensi* mereka, atau dengan kata lain, urutan prioritas default operator.\n\nDari sekolah, kita semua tahu bahwa perkalian dalam expresi `1 + 2 * 2` harus dihitung sebelum penambahan. Itulah arti dari presedensi. Perkalian disebut memiliki *presedensi lebih tinggi* dari penambahan.\n\nTanda kurung mengesampingkan presedensi apapun, jadi jika kita tak puas dengan urutan default, kita bisa gunakan mereka untuk mengubahnya. Misalnya: tulis `(1 + 2) * 2`.\n\nAda banyak operator di JavaScript. Tiap operator punya nomor presedensi masing-masing. Nomor yang lebih besar dieksekusi terlebih dahulu. Jika presedensinya sama, urutan eksekusinya dari kiri ke kanan.\n\nDi sini adalah extrak dari [tabel presedensi](https://developer.mozilla.org/en/JavaScript/Reference/operators/operator_precedence) (kamu tak usah mengingat ini, tapi catat bahwa operator unary lebih tinggi dari binary terkait):\n\n| Presedensi | Nama | Tanda |\n|------------|------|------|\n| ... | ... | ... |\n| 17 | plus unary | `+` |\n| 17 | negasi unary | `-` |\n| 16 | akar pangkat | `**` |\n| 15 | perkalian | `*` |\n| 15 | pembagian | `/` |\n| 13 | penambahan | `+` |\n| 13 | pengurangan | `-` |\n| ... | ... | ... |\n| 3 | penetapan | `=` |\n| ... | ... | ... |\n\nSeperti yang kita lihat, \"plus unary\" punya prioritas `17` yang lebih tinggi dari `13` \"penambahan\" (plus binary). Itulah kenapa, dalam expresi `\"+apples + +oranges\"`, plus unary bekerja sebelum penambahan.\n\n## Penetapan\n\nMari ingat bahwa penetapan `=` juga merupakan operator. Ia terdaftar di tabel presedensi dengan prioritas sangat rendah `3`.\n\nItulah kenapa, ketika kita tetapkan variabel, seperti `x = 2 * 2 + 1`, kalkulasinya dilakukan pertama dan kemudian `=` dievaluasi, menyimpan hasilnya dalam in `x`.\n\n```js\nlet x = 2 * 2 + 1;\n\nalert( x ); // 5\n```\n\n### Assignment = mengembalikan nilai\n\nFakta dari `=` menjadi sebuah operator, bukanlah sebuah hal yang \"fantastis\" konstruksi dari bahasa memiliki implikasi yang menarik.\n\nKebanyakan operator di Javascript mengembalikan sebuah nilai. Sudah jelas untuk `+` dan `-`, tetapi berlaku juga untuk `=`.\n\nPanggilan `x = value` menulis `value` ke dalam `x` *dan mengembalikannya*.\n\nIni adalah demo yang menggunakan penetapan sebagai bagian dari expresi yang rumit:\n\n```js run\nlet a = 1;\nlet b = 2;\n\n*!*\nlet c = 3 - (a = b + 1);\n*/!*\n\nalert( a ); // 3\nalert( c ); // 0\n```\n\nDi contoh di atas, hasil dari expresi `(a = b + 1)` ialah nilai yang ditetapkan ke `a` (yaitu `3`). Ia kemudian digunakan untuk evaluasi berikutnya.\n\nKodenya lucu, kan? kita harus mengerti bagaimana itu bekerja, karena terkadang kita melihat hal itu didalam library Javascript.\n\nDan juga, tolong jangan tulis kode seperti itu. Trik semacam itu tidak akan membuat kode menjadi jelas dan mudah dibaca.\n\n### Chaining assignments / Assignments berantai\n\nFitur menarik lainnya adalah kemampuan untuk melakukan assignments berantai:\n\n```js run\nlet a, b, c;\n\n*!*\na = b = c = 2 + 2;\n*/!*\n\nalert( a ); // 4\nalert( b ); // 4\nalert( c ); // 4\n```\n\nAssignments berantai mengevaluasi dari kanan ke kiri. Pertama, dari ekspresi paling kanan `2 + 2` di evaluasi terlebih dahulu dan dimasukan kedalam variabel disebelah kiri: `c`, `b` dan `a`. Dan diakhir, seluruh variabel saling membagi satu nilai.\n\nSekali lagi, untuk tujuan kode yang mudak dibaca akan lebih baik untuk membagi kode kedalam beberapa baris:\n\n```js\nc = 2 + 2;\nb = c;\na = c;\n```\nIni akan mudah untuk dibaca, terutama jika melihatnya secara sekilas.\n\n## Mengubah variabel secara langsung\n\nKita terkadang membutuhkan sebuah operator untuk sebuah variabel dan menyimpan hasil baru didalam variabel yang sama\n\nContoh:\n\n```js\nlet n = 2;\nn = n + 5;\nn = n * 2;\n```\n\nNotasi ini bisa diperpendek dengan menggunakan operator `+=` dan `*=`:\n\n```js run\nlet n = 2;\nn += 5; // sekarang n = 7 (sama dengan n = n + 5)\nn *= 2; // sekarang n = 14 (sama dengan n = n * 2)\n\nalert( n ); // 14\n```\n\nOperator dari \"ubah-dan-simpan\" atau bisa disebut mengubah variabel secara langsung hadir pada setiap aritmatik dan operator bitwise: `/=`, `-=`, etc.\n\nOperator semacam itu memiliki hak dengan tingkat yang sama dengan assignment yang biasa, jadi mereka akan berjalan setelah kalkulasi lainnya selesai:\n\n```js run\nlet n = 2;\n\nn *= 3 + 5;\n\nalert( n ); // 16  (bagian paling kanan dievaluasi terlebih dahulu, sama seperti n *= 8)\n```\n\n## Inkremen/dekremen\n\n<!-- Tak bisa menggunakan \"--/++\" di judul, karena built-in parse mengubahnya menjadi sebuah \"dash\"/- -->\n\nMenaikkan atau menurunkan satu angka ialah salah satu operasi numerik paling umum.\n\nJadi, ada operator spesial untuk itu:\n\n- **Inkremen** `++` menaikkan variabel sebanyak 1:\n\n    ```js run no-beautify\n    let counter = 2;\n    counter++;        // cara kerjanya sama dengan counter = counter + 1, tapi lebih pendek\n    alert( counter ); // 3\n```\n- **Decrement** `--` menurunkan variabel sebanyak 1:\n\n    ```js run no-beautify\n    let counter = 2;\n    counter--;        // cara kerjanya sama dengan counter = counter - 1,tapi lebih pendek\n    alert( counter ); // 1\n    ```\n\n```warn\nInkremen/dekremen cuma bisa diaplikasikan ke variabel. Mencoba menggunakan itu pada nilai seperti `5++` akan menghasilkan galat.\n​```\n\nOperator `++` dan `--` bisa ditaruh sebelum atau setelah variabel.\n\n- Ketika operatornya ditaruh setelah variabel, ia ada dalam \"bentuk postfix\": `counter++`.\n- \"Bentuk prefix\" ialah ketika operatornya ditaruh sebelum variabel: `++counter`.\n\nKedua pernyataan ini melakukan hal yang sama: menambah `counter` sebanyak `1`.\n\nApakah ada perbedaan? Ya, tapi kita cuma bisa melihatnya jika kita menggunakan nilai kembalian `++/--`.\n\nMari kita klarifikasi. Seperti yang kita tahu, semua operator mengembalikan nilai. Inkremen/dekremen bukan pengecualian. Bentuk prefix mengembalikan nilai baru sedangkan bentuk postfix mengembalikan nilai lama (sebelum inkremen/dekremen).\n\nUntuk melihat perbedaannya, berikut misalnya:\n\n```js run\nlet counter = 1;\nlet a = ++counter; // (*)\n\nalert(a); // *!*2*/!*\n​```\n\nDalam baris `(*)`, bentuk *prefix*`++counter` menginkremen `counter` dan mengembalikan nilai baru, `2`. Jadi, `alert` menampilkan `2`.\n\nSekarang, mari kita gunakan bentuk postfix:\n\n```js run\nlet counter = 1;\nlet a = counter++; // (*) ganti ++counter ke counter++\n\nalert(a); // *!*1*/!*\n​```\n\nDalam baris `(*)`, bentuk *postfix* `counter++` juga menginkremen `counter` tapi mengembalikan nilai *lama* (sebelum inkremen). Jadi, `alert` menampilkan `1`.\n\nRingkasnya:\n\n- Jika hasil dari inkremen/dekremen tak digunakan, tak ada bedanya bentuk mana yang dipakai:\n\n    ```js run\n    let counter = 0;\n    counter++;\n    ++counter;\n    alert( counter ); // 2, kedua counter diatas melakukan hal yang serupa.\n    ```\n- Jika kita ingin menaikkan nilai *dan* langsung memakai hasil dari operator, kita butuh bentuk prefix:\n\n    ```js run\n    let counter = 0;\n    alert( ++counter ); // 1\n    ```\n- Jika kita ingin menginkremen suatu nilai tanpa memakai nilai sebelumnya, kita butuh bentuk postfix:\n\n    ```js run\n    let counter = 0;\n    alert( counter++ ); // 0\n    ```\n\n````smart header=\"Inkremen/dekremen di antara operator lainnya\"\nOperator `++/--` bisa juga digunakan di dalam expresi. Presedensi mereka lebih tinggi dari kebanyakan operasi aritmatika lainnya.\n\nMisalnya:\n\n```js run\nlet counter = 1;\nalert( 2 * ++counter ); // 4\n​```\n\nBandingkan dengan:\n\n```js run\nlet counter = 1;\nalert( 2 * counter++ ); // 2, karena counter++ mengembalikan nilai \"lama\"\n​```\n\nMeski secara teknis OK, notasi macam ini biasanya membuat kode kurang dapat dibaca. Satu baris melakukan banyak hal -- tak baik.\n\nSambil membaca kode, dan melihatnya secara sekilas kita bisa saja melewatkan kode seperti `counter++` dan tidak akan jelas bahwa nilai variabel telah bertambah.\n\nJadi direkomendasikan menuliskan kode dengan gaya \"satu baris -- satu aksi\":\n\n```js run\nlet counter = 1;\nalert( 2 * counter );\ncounter++;\n​```\n````\n\n## Operator bitwise\n\nOperator bitwise memperlakukan argumen sebagai angka integer 32-bit dan bekerja pada level representasi biner mereka.\n\nOperator ini bukan spesifik JavaScript. Mereka didukung di banyak bahasa pemrograman.\n\nDaftar operator:\n\n- AND ( `&` )\n- OR ( `|` )\n- XOR ( `^` )\n- NOT ( `~` )\n- LEFT SHIFT ( `<<` )\n- RIGHT SHIFT ( `>>` )\n- ZERO-FILL RIGHT SHIFT ( `>>>` )\n\nOperator seperti diatas sangat jarang digunakan, ketika kita membutuhkan untuk memainkan angka di level paling rendah (bitwise). Kita tidak akan membutuhkan operator seperti ini dalam waktu dekat, sebagaimana dalam pengembangan web penggunaan operator seperti itu lebih sedikit, tetapi di area yang spesial, seperti kriptographi, operator seperti itu sangan dibutuhkan. Kamu bisa membaca artikel [Bitwise Operators](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators) di MDN ketika kamu membutuhkannya.\n\n## Koma\n\nOperator koma `,` ialah satu dari banyak operator paling langka dan tak biasa. Kadang, ia digunakan untuk menulis kode lebih pendek, jadi kita harus tahu itu untuk memahami apa yang terjadi.\n\nOperator koma memperbolehkan untuk mengevaluasi beberapa expresi, membagi mereka dengan koma `,`. Each of them is evaluated but only the result of the last one is returned.\n\nMisalnya:\n\n```js run\n*!*\nlet a = (1 + 2, 3 + 4);\n*/!*\n\nalert( a ); // 7 (hasil dari 3 + 4)\n```\n\nDi sini, expresi pertama `1 + 2` dievaluasi dan hasilnya dibuang. Lalu, `3 + 4` dievaluasi dan dikembalikan sebagai hasilnya.\n\n```smart header=\"Koma punya presedensi sangat kecil\"\nHarap ingat bahwa operator koma punya presedensi sangat kecil, lebih kecil dari `=`, jadi tanda kurung penting dalam contoh di atas.\n\nTanpa mereka: `a = 1 + 2, 3 + 4` mengevaluasi `+` terlebih dahulu, menjumlahkan mereka menjadi `a = 3, 7`, lalu operator penetapan `=` menetapkan `a = 3`, dan sisanya diabaikan. Ini seperti `(a = 1 + 2), 3 + 4`.\n```\n\nKenapa kita butuh operator yang membuang semuanya kecuali expresi terakhir?\n\nKadang, orang memakai itu dalam konstruksi rumit untuk menaruh beberapa aksi dalam satu baris.\n\nMisalnya:\n\n```js\n// tiga operasi dalam satu baris\nfor (*!*a = 1, b = 3, c = a * b*/!*; a < 10; a++) {\n ...\n}\n```\n\nTrik macam ini dipakai di banyak framework JavaScript. Itulah kenapa kita membahas mereka. Tapi, biasanya, mereka tak membuat kode mudah dibaca sehingga kita sebaiknya pikir-pikir dulu sebelum menggunakan mereka.\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/1-comparison-questions/solution.md",
    "content": "\n\n```js no-beautify\n5 > 4 → true\n\"apple\" > \"pineapple\" → false\n\"2\" > \"12\" → true\nundefined == null → true\nundefined === null → false\nnull == \"\\n0\\n\" → false\nnull === +\"\\n0\\n\" → false\n```\n\nBeberapa alasan:\n\n1. Sudah jelas, true.\n2. Pembandingan kamus, jadi false. `\"a\"` lebih kecil dari `\"p\"`.\n3. Lagi, pembandingan kamus, karakter pertama `\"2\"` lebih besar dari karakter pertama `\"1\"`.\n4. Nilai `null` dan `undefined` selalu bernilai sama.\n5. Equalitas ketat memang ketat. Tipe berbeda dari kedua sisi menghasilkan false.\n6. Serupa dengan `(4)`, `null` hanya sama dengan `undefined`.\n7. Equalitas ketat dari tipe berbeda.\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/1-comparison-questions/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Pembandingan\n\nApa hasil dari expresi ini?\n\n```js no-beautify\n5 > 4\n\"apple\" > \"pineapple\"\n\"2\" > \"12\"\nundefined == null\nundefined === null\nnull == \"\\n0\\n\"\nnull === +\"\\n0\\n\"\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/09-comparison/article.md",
    "content": "# Perbandingan\n\nKita tahu beberapa operator pembanding dari matematika.\n\nDidalam Javascript operator-operator itu ditulis seperi ini:\n\n- Lebih besar/kurang dari: <code>a &gt; b</code>, <code>a &lt; b</code>.\n- Lebih besar/kurang dari atau sama: <code>a &gt;= b</code>, <code>a &lt;= b</code>.\n- Sama dengan: `a == b`, perhatikan tanda dua `=` digunakan untuk test persamaan, jika menggunakan satu `=` seperti `a = b` itu adalah sebuah asignment atau memasukan nilai kedalam variabel.\n- Tidak sama dengan: Didalam matematika notasinya seperti <code>&ne;</code>, tetapi didalam Javascript ditulis seperti <code>a != b</code>.\n\nDidalam artikel ini kita akan belajar lebih lanjut tentang perbedaan tipe dari perbandingan, bagaimana cara Javascript membuatnya, termasuk sifat-sifat penting.\n\nDiakhir nanti kamu akan menemukan hal yang bagus untuk menghindari masalah yang berhubungan dengan \"kebiasaan Javascript\".\n\n## Boolean ialah hasilnya\n\nSemua operator pembanding mengembalikan nilai boolean:\n\n- `true` -- berarti \"ya\", \"betul\" atau \"fakta\".\n- `false` -- berarti \"tidak\", \"salah\" atau \"bukan fakta\".\n\nMisalnya:\n\n```js run\nalert( 2 > 1 );  // true (benar)\nalert( 2 == 1 ); // false (salah)\nalert( 2 != 1 ); // true (benar)\n```\n\nHasil perbandingan bisa ditetapkan ke variabel, sama seperti nilainya:\n\n```js run\nlet result = 5 > 4; // tetapkan hasil perbandingan\nalert( result ); // true\n```\n\n## Perbandingan string\n\nUntuk melihat apakah satu string lebih besar dari yang lain, JavaScript menggunakan yang disebut \"kamus\" atau urutan \"lexicografik\".\n\nDengan keta lain, string diperbandingkan huruf-demi-huruf.\n\nMisalnya:\n\n```js run\nalert( 'Z' > 'A' ); // true\nalert( 'Glow' > 'Glee' ); // true\nalert( 'Bee' > 'Be' ); // true\n```\n\nalgoritma untuk membandingkan dua string sederhana:\n\n1. Bandingkan karakter pertama dari kedua string.\n2. Jika karakter pertama dari string pertama lebih besar (atau lebih kecil) dari string kedua, maka string pertama lebih besar (atau lebih kecil) dari string kedua. Kita selesai.\n3. Sebaliknya, jika karakter pertama kedua string sama, bandingkan karakter kedua dengan cara sama.\n4. Ulangi sampai string berakhir.\n5. Jika kedua string berakhir pada panjang yang sama, maka mereka sama. Sebaliknya, string lebih panjang yang lebih besar.\n\nPada contoh di atas, pembandingan `'Z' > 'A'` menghasilkan pada langkah pertama sedangkan string `\"Glow\"` dan `\"Glee\"` dibandingkan karakter-demi-karakter:\n\n1. `G` sama dengan `G`.\n2. `l` sama dengan `l`.\n3. `o` lebih besar dari `e`. Berhenti di sini. String pertama lebih besar.\n\n```smart header=\"Bukan dictionary riil, tapi urutan Unicode\"\nAlgoritma pembangingan yang diberikan di atas secara kasar equivalen dengan yang digunakan dalam kamus atau buku telpon, tapi tak sepenuhnya sama.\n\nMisalnya, case diperhitungkan. Huruf besar `\"A\"` tak sama dengan huruf kecil `\"a\"`. Yang mana yang lebih besar? Huruf kecil `\"a\"`. Kenapa? Karena karakter huruf kecil punya index lebih besar dalam tabel encoding internal yang dipakai JavaScript (Unicode). Kita akan kembali ke detil spesifik dan konsekuensinya dalam bab <info:string>.\n```\n\n## Pembandingan dari tipe yang berbeda\n\nKetika membandingkan nilai dari tipe yang berbeda, JavaScript mengkonversi nilai tersebut ke angka.\n\nMisalnya:\n\n```js run\nalert( '2' > 1 ); // true, string '2' menjadi angka 2\nalert( '01' == 1 ); // true, string '01' menjadi angka 1\n```\n\nUntuk nilai boolean, `true` menjadi `1` dan `false` menjadi `0`. \n\nMisalnya:\n\n```js run\nalert( true == 1 ); // true\nalert( false == 0 ); // true\n```\n\n````smart header=\"Konsekuensi lucu\"\nMemungkinkan juga bahwa pada saat yang sama:\n\n- Dua nilai equal.\n- Satu dari mereka `true` sebagai boolean dan satunya lagi `false` sebagai boolean.\n\nMisalnya:\n\n```js run\nlet a = 0;\nalert( Boolean(a) ); // false\n\nlet b = \"0\";\nalert( Boolean(b) ); // true\n\nalert(a == b); // true!\n```\n\nDari cara pandang JavaScript', hasil ini terbilang normal. Pengecekan equalitas mengkonversi nilai menggunakan konversi numerik (jadi `\"0\"` menjadi `0`), sedangkan konversi explisit `Boolean` menggunakan set aturan yang lain.\n````\n\n## Equalitas ketat\n\nPengecekan equalitas reguler `==` punya satu problem. Ia tak bisa membedakan `0` dari `false`:\n\n```js run\nalert( 0 == false ); // true\n```\n\nHal yang sama terjadi pada string kosong:\n\n```js run\nalert( '' == false ); // true\n```\n\nIni terjadi karena operand dari tipe yang berbeda dikonversi ke angka oleh operator equalitas `==`. String kosong, sama seperti `false`, menjadi nol.\n\nApa yang dilakukan jika kita ingin membedakan `0` dari `false`?\n\n**Operator equalitas ketat `===` mengecek equalitas tanpa konversi tipe.**\n\nDengan kata lain, jika `a` dan `b` tipenya berbeda, maka `a === b` segera menghasilkan `false` tanpa konversi apapun terhadap mereka.\n\nMari kita coba:\n\n```js run\nalert( 0 === false ); // false, karena tipenya berbeda\n```\n\nAda juga operator \"non-equalitas ketat\" `!==` yang analog dengan `!=`.\n\nOperator equalitas ketat sedikit lebih panjang untuk ditulis, tapi lebih memperlihatkan apa yang terjadi dan meninggalkan ruang lebih kecil untuk galat.\n\n## Pembandingan dengan null dan undefined\n\nAda sikap non-intuitif ketika `null` atau `undefined` diperbandingkan dengan nilai lain.\n\nUntuk pengecekan equalitas ketat `===`\n: Nilai ini berbeda, karena setiap dari mereka tipenya berbeda.\n\n    ```js run\n    alert( null === undefined ); // false\n    ```\n\nUntuk pengecekan non-ketat `==`\n: Ada aturan spesial. Kedyanya merupakan \"pasangan manis\": mereka sama (karena `==`), tapi tidak dengan nilai lain manapun.\n\n    ```js run\n    alert( null == undefined ); // true\n    ```\n\nUntuk pembandingan matematika dan lainnya `< > <= >=`\n: `null/undefined` dikonversi ke angka: `null` menjadi `0`, sedangkan `undefined` menjadi `NaN`.\n\nSekarang mari kita lihat beberapa hal lucu yang terjadi ketika kita menerapkan aturan ini. Dan, yang lebih penting, bagaimana supaya tidak jatuh ke dalam perangkap ini.\n\n### Hasil aneh: null vs 0\n\nMari kita bandingkan `null` dengan nol:\n\n```js run\nalert( null > 0 );  // (1) false\nalert( null == 0 ); // (2) false\nalert( null >= 0 ); // (3) *!*true*/!*\n```\n\nSecara matematik, ini aneh. Hasil terakhir menyatakan bahwa \"`null` lebih besar atau sama dengan nol\", seharusnya salah satu dari pembandingan di atasnya `true`, tapi nyatanya malah tidak.\n\nAlasannya ialah pengecekan equalitas `==` dan pembandingan `> < >= <=` bekerja secara berbeda. Pembandingan mengkonversi `null` ke angka `0`. Itulah kenapa (3) `null >= 0` true dan (1) `null > 0` false.\n\nDi sisi lain, pengecekan equalitas `==` untuk `undefined` and `null` dijelaskan bahwa, tanpa konversi apapun, mereka sama dengan satu sama lain dan tidak sama dengan nilai lain manapun. Itulah kenapa (2) `null == 0` is false.\n\n### Uundefined yang tak bisa diperbandingkan\n\nNilai `undefined` sebainya tak diperbandingkan dengan nilai lain:\n\n```js run\nalert( undefined > 0 ); // false (1)\nalert( undefined < 0 ); // false (2)\nalert( undefined == 0 ); // false (3)\n```\n\nKenapa ia tak suka sekali dengan nol? Selalu false!\n\nKita dapatkan hasil ini karena:\n\n- Pembandingan `(1)` dan `(2)` menghasilkan `false` karena `undefined` dikonversi ke `NaN` dan `NaN` merupakan numerik spesial yang mengembalikan `false` untuk semua pembandingan.\n- Pengecekan equalitas `(3)` mengembalikan `false` karena `undefined` hanya sama dengan `null` dan tidak dengan nilai lain manapun.\n\n### Hindari problem\n\nKenapa kita menggunakan contoh ini? Haruskah kita ingat semua keanehan ini? Tidak juga. Sebenarnya, hal tricky macam ini akan menjadi akrab seiring waktu, tapi ada cara solid untuk menghindari masalah dengan mereka:\n\nPerlakukan pembandingan manapun dengan `undefined/null` kecuali equalitas ketat `===` dengan hati-hati.\n\nJangan gunakan pembandingan `>= > < <=` dengan variabel yang bisa jadi `null/undefined`, kecuali kamu paham apa yang kamu lakukan. Jika variabel bisa punya nilai ini, cek mereka secara terpisah.\n\n## Kesimpulan\n\n- Operator pembandingan menghasilkan nilai boolean.\n- String dibandingkan huruf-demi-huruf dalam urutan \"kamus\".\n- Ketika nilai dari tipe berbeda diperbandingkan, mereka dikonversi ke angka (kecuali pengecekan equalitas ketat).\n- Nilai `null` dan `undefined` sama dengan `==` satu sama lain dan tidak sama dengan nilai lain manapun.\n- Waspada ketika menggunakan pembandingan seperti `>` atau `<` dengan variabel yang kadang bisa jadi `null/undefined`. Pengecekan secara terpisah `null/undefined` merupakan ide yang bagus.\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/1-if-zero-string/solution.md",
    "content": "**Ya, tentu saja.**\n\nSetiap string kecuali yang kosong (ingat bahwa `\" 0 \"` tidak kosong) menjadi `true` dalam konteks logika boolean.\n\nKita dapat mengeksekusi dan mengecek kode di bawah ini :\n\n```js run\nif (\"0\") {\n  alert( 'Hello' );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/1-if-zero-string/task.md",
    "content": "Nilai penting: 5\n\n---\n\n# if (string berisi angka nol)\n\nApakah `alert` akan dieksekusi?\n\n```js\nif (\"0\") {\n  alert( 'Hello' );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/ifelse_task2/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n  <script>\n    'use strict';\n\n    let value = prompt('Apakah nama \"official\" dari Javascript?', '');\n\n    if (value == 'ECMAScript') {\n      alert('Benar sekali!');\n    } else {\n      alert(\"Kamu tidak tahu? ECMAScript!\");\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/solution.md",
    "content": "\n\n[html run src=\"ifelse_task2/index.html\"]\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/2-check-standard/task.md",
    "content": "Nilai penting: 2\n\n---\n\n# Nama JavaScript\n\nDengan menggunakan konstruksi `if..else`, tulis kode yang menanyakan: 'Apa nama \"official\" JavaScript?'\n\nJika pengunjung memasukkan \"ECMAScript\", maka tunjukkan output \"Benar!\", Jika tidak - tunjukkan output: \"Tidak tahu? ECMAScript!\"\n\n![](ifelse_task2.svg)\n\n[demo src=\"ifelse_task2\"]\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/if_sign/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<body>\n\n  <script>\n    'use strict';\n\n    let value = prompt('Tuliskan sebuah angka', 0);\n\n    if (value > 0) {\n      alert(1);\n    } else if (value < 0) {\n      alert(-1);\n    } else {\n      alert(0);\n    }\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/solution.md",
    "content": "\n\n```js run\nlet value = prompt('Masukan sebuah angka', 0);\n\nif (value > 0) {\n  alert( 1 );\n} else if (value < 0) {\n  alert( -1 );\n} else {\n  alert( 0 );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/3-sign/task.md",
    "content": "Nilai penting: 2\n\n---\n\n# Tunjukkan tandanya\n\nDengan menggunakan `if..else`, tulis kode yang mendapatkan nomor melalui` prompt` dan kemudian tampilkan nomornya dengen menggunakan `alert`:\n\n- `1`, jika nilainya lebih besar dari nol,\n- `-1`, jika kurang dari nol,\n- `0`, jika sama dengan nol.\n\nDalam tugas ini kita mengasumsikan bahwa input selalu berupa angka.\n\n[demo src=\"if_sign\"]\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/5-rewrite-if-question/solution.md",
    "content": "\n\n```js\nlet result = (a + b < 4) ? 'Kurang' : 'Lebih';\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/5-rewrite-if-question/task.md",
    "content": "Nilai penting: 5\n\n---\n\n# Tulis ulang 'if' menggunakan '?'\n\nTulis ulang `if` menggunakan operator kondisional `'?'`:\n\n```js\nlet result;\n\nif (a + b < 4) {\n  result = 'Kurang';\n} else {\n  result = 'Lebih';\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/solution.md",
    "content": "\n\n```js\nlet message = (login == 'Karyawan') ? 'Hallo' :\n  (login == 'Direksi') ? 'Salam Hangat!' :\n  (login == '') ? 'Belum Login' :\n  '';\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/6-rewrite-if-else-question/task.md",
    "content": "Nilai penting: 5\n\n---\n\n# Tulis ulang 'if..else' menjadi '?'\n\nTulis ulang `if..else` menggunakan beberapa operator ternary` '?' `.\n\nAgar mudah dibaca, disarankan untuk membagi kode menjadi beberapa baris.\n\n```js\nlet message;\n\nif (login == 'Karyawan') {\n  message = 'Hallo';\n} else if (login == 'Direksi') {\n  message = 'Salam Hangat!';\n} else if (login == '') {\n  message = 'Belum Login';\n} else {\n  message = '';\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/10-ifelse/article.md",
    "content": "# Kondisi bercabang: if, '?'\n\nTerkadang, kita perlu melakukan tindakan berbeda berdasarkan kondisi yang berbeda.\n\nUntuk melakukan itu, kita dapat menggunakan pernyataan  `if`  dan operator kondisional `?`, yang juga merupakan \"question mark\" operator (operator tanda tanya).\n\n## Pernyataan \"if\"\n\nPernyataan `if` mengevaluasi suatu kondisi dan, jika hasil kondisi itu `true`, maka blok kode di dalam `if` akan diexekusi.\n\nSebagai contoh:\n\n```js run\nlet tahun = prompt('Ditahun berapa spesifikasi ECMAScript-2015 dipublikasikan?', '');\n\n\n*!*\nif (tahun == 2015) alert( 'Kamu Benar!' );\n*/!*\n```\n\nPada contoh di atas, persyaratannya adalah pemeriksaan kesetaraan sederhana (`tahun == 2015`), tetapi pada kode lain, bisa menjadi jauh lebih kompleks.\n\nJika kita ingin menjalankan lebih dari satu pernyataan di dalam suatu kondisional blok kode, kita harus membungkus blok kode kita di dalam kurung kurawal {} :\n\n```js\nif (tahun == 2015) {\n  alert( \"Kamu Benar!\" );\n  alert( \"Kamu Memang Pintar!\" );\n}\n```\n\nDisarankan untuk membungkus blok kode Anda dengan kurung kurawal `{}` setiap kali Anda menggunakan pernyataan `if`, bahkan jika hanya ada satu pernyataan di dalam suatu blok kode. Hal ini agar kode anda mudah dibaca oleh anda di masa depan,dan tentu saja oleh orang lain.\n\n## Konversi Boolean\n\nPernyataan `if (…)` mengevaluasi ekspresi dalam tanda kurung dan mengubah hasilnya menjadi boolean.\n\nMari kita mengingat kembali aturan konversi dari bab <info:type-conversions>:\n\n- Angka `0`, string kosong `\"\"`, `null`, `undefined`, dan `NaN` semuanya menjadi salah (`false`). Oleh sebab itu,mereka disebut nilai-nilai \"falsy\" (palsu/salah)\n- Nilai-nilai yang lain menjadi benar (`true`), sehingga kadang mereka disebut \"truthy\" (benar)\n\nJadi, kode dalam kondisi ini tidak akan pernah berjalan:\n\n```js\nif (0) { // 0 adalah falsy\n  ...\n}\n```\n\n... dan di dalam kondisi ini - selalu akan berjalan:\n\n```js\nif (1) { // 1 adalah truthy\n  ...\n}\n```\n\nKamu juga dapat memberikan nilai boolean yang telah dievaluasi ke  `if`, seperti ini::\n\n```js\nlet kondisi = (tahun == 2015); // mengevaluasi nilai apakah *true* atau *false*\n\nif (kondisi) {\n  ...\n}\n```\n\n## Klausa \"else\"\n\nPernyataan `if` dapat berisi blok opsional \"else\" opsional. Block \"else\" dijalankan ketika semua kondisi di atas blok \"else\" salah (false) semua.\n\nContohnya:\n```js run\nlet tahun = prompt('Ditahun berapa spesifikasi ECMAScript-2015 dipublikasikan?'', '');\n\nif (tahun == 2015) {\n  alert( 'Jawaban kamu benar!' );\n} else {\n  alert( 'Hah, kok jawaban kamu salah?' ); // nilai lainnya selain 2015\n}\n```\n\n## Pernyataan kondisional lebih dari satu: \"else if\"\n\nTerkadang,  kita ingin menguji beberapa kondisi yang berbeda-beda Klausa `else if` memungkinkan kita melakukan itu.\n\nSebagai contoh:\n\n```js run\nlet tahun = prompt('Ditahun berapa spesifikasi ECMAScript-2015 dipublikasikan?', '');\n\nif (tahun < 2015) {\n  alert( 'Terlalu dini...' );\n} else if (tahun > 2015) {\n  alert( 'Terlalu akhir' );\n} else {\n  alert( 'Tepat sekali!' );\n}\n```\n\nPada kode di atas, pertama JavaScript mengecek ekspresi `tahun < 2015`. Jika itu salah, maka masuk ke kondisi selanjutnya `tahun > 2015`. Jika itu juga salah, pernyataan di dalam blok \"else\" akan dijalankan,yang merupakan sebuah alert\n\nBlok `else if`bisa digunakan berkali-kali. Pernyataan final `else` hanya opsional.\n\n## Operator Kondisional '?'\n\nTerkadang, kita perlu memberi nilai ke suatu variabel tergantung pada suatu kondisi.\n\nContohnya:\n\n```js run no-beautify\nlet accessAllowed;\nlet umur = prompt('Berapakah umur kamu?', '');\n\n*!*\nif (umur > 18) {\n  accessAllowed = true;\n} else {\n  accessAllowed = false;\n}\n*/!*\n\nalert(accessAllowed);\n```\n\nOperator \"question mark\" (tanda tanya) memungkinkan kita melakukan kode di atas dengan cara yang lebih singkat dan sederhana.\n\nOperator tersebut direpresentasikan oleh tanda tanya `?`. Nama lain dari operator ini adalah \"ternary\", karena operator ini memiliki tiga operan (ternary bahasa Indonesia adalah \"terdiri dari 3 bagian\"). Ini sebenarnya satu-satunya operator dalam JavaScript yang memiliki 3 operan.\n\nSintaksnya adalah:\n```js\nlet result = condition ? value1 : value2;\n```\n\nDi kode di atas, `condition` dievaluasi: jika itu benar maka value1 akan dikembalikan, jika tidak - value2 yang akan dikembalikan.\n\nContohnya:\n\n```js\nlet accessAllowed = (age > 18) ? true : false;\n```\n\nSecara teknis, kita dapat menghilangkan tanda kurung di sekitar `age > 18` . Operator tanda tanya memiliki prioritas rendah, sehingga hanya akan dijalankan setelah perbandingan `>` .\n\nContoh di bawah ini akan melakukan hal yang sama seperti kode sebelumnya:\n\n```js\n// perbandingan operator \"age > 18\" dieksekusi pertama kali\n// (tidak perlu dibungkus dengan kurung)\nlet accessAllowed = age > 18 ? true : false;\n```\n\nTetapi tanda kurung membuat kode lebih mudah dibaca, jadi kami sarankan untuk tetap menggunakannya di kode-kode anda.\n\n````smart\nPada contoh di atas, Anda dapat menghindari pengunaan operator tanda tanya karena perbandingan itu sendiri menghasilkan  `true/false`:\n\n```js\n// the same\nlet accessAllowed = age > 18;\n```\n````\n\n## Multiple '?'\n\nRangkaian operator tanda tanya `?` dapat mengembalikan nilai yang tergantung pada lebih dari satu kondisi.\n\nContohnya:\n```js run\nlet age = prompt('age?', 18);\n\nlet message = (age < 3) ? 'Hi, baby!' :\n  (age < 18) ? 'Hello!' :\n  (age < 100) ? 'Greetings!' :\n  'What an unusual age!';\n\nalert( message );\n```\n\nMungkin sulit pada awalnya untuk memahami apa yang terjadi. Tetapi setelah melihat lebih dekat, kita dapat melihat bahwa itu hanya serangkaian tes biasa:\n\n1. Tanda tanya pertama memeriksa apakah `age < 3`.\n2. Jika benar -- `'Hi, baby!'` akan dikembalikan. Jika tidak, kode akan melanjutkan ke ekspresi setelah titik dua '\":\"', memeriksa apakah `age < 18`.\n3. Jika itu benar -- `'Hello!'` akan dikembalikan. . Jika tidak, kode akan melanjutkan ke ekspresi setelah titik dua berikutnya '\":\"', memeriksa `age < 100`.\n4. Jika itu benar -- `'Greetings!'` akan dikembalikan. Jika tidak, kode akan melanjutkan ke ekspresi setelah titik dua terakhir  '\":\"', dan akhirnya akan mengembalikan `'What an unusual age!'`.\n\nKode bawah ini memperlihatkan apabila menggunakan `if..else` untuk kode di atas:\n\n```js\nif (age < 3) {\n  message = 'Hi, baby!';\n} else if (age < 18) {\n  message = 'Hello!';\n} else if (age < 100) {\n  message = 'Greetings!';\n} else {\n  message = 'What an unusual age!';\n}\n```\n\n## Penggunaan '?' yang non-tradisional\n\nTerkadang tanda tanya `?` digunakan sebagai pengganti `if`:\n\n```js run no-beautify\nlet company = prompt('Which company created JavaScript?', '');\n\n*!*\n(company == 'Netscape') ?\n   alert('Right!') : alert('Wrong.');\n*/!*\n```\n\nBergantung pada kondisional `company == 'Netscape' , ekspresi pertama atau kedua setelah `?` akan dieksekusi dan menunjukkan sebuah \"alert\".\n\nKita tidak memberikan nilai hasil ke suatu variable di sini. Sebagai gantinya, kita mengeksekusi kode yang berbeda tergantung pada kondisinya.\n\n**Tidak disarankan memakai operator tanda tanya dengan cara ini.**\n\nNotasinya memang lebih pendek daripada apabila menggunakan pernyataan `if` , yang mungkin menarik bagi beberapa programmer, tetapi hal ini membuat kode anda lebih susah dibaca.\n\nBerikut adalah kode yang sama menggunakan `if` untuk perbandingan:\n\n```js run no-beautify\nlet company = prompt('Which company created JavaScript?', '');\n\n*!*\nif (company == 'Netscape') {\n  alert('Right!');\n} else {\n  alert('Wrong.');\n}\n*/!*\n```\n\nMata kita memindai kode secara vertikal. Blok kode yang terdiri dari beberapa baris akan lebih mudah dipahami daripada suatu set instruksi horizontal yang panjang.\n\nTujuan operator tanda tanya `?` adalah mengembalikan satu nilai atau nilai yang lainnya tergantung pada kondisinya. Mohon untuk gunakan operator tanda tanya hanya untuk fungsi tersebut. Gunakan `if ketika Anda perlu menjalankan berbagai cabang kode yang berbeda-beda\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/solution.md",
    "content": "Jawabannya `2`, itu nilai truthy pertama.\n\n```js run\nalert( null || 2 || undefined );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/1-alert-null-2-undefined/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Apa hasil dari OR?\n\nApakah keluaran dari kode dibawah?\n\n```js\nalert( null || 2 || undefined );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/2-alert-or/solution.md",
    "content": "Jawabannya: pertama `1`, lalu `2`.\n\n```js run\nalert( alert(1) || 2 || alert(3) );\n```\n\nPanggilan `alert` tak mengembalikan nilai. Atau, dengan kata lain, ia mengembalikan `undefined`.\n\n1. Pertama OR `||` mengevaluasi operand kiri `alert(1)`. Ia menampilkan pesan pertama dengan `1`.\n2. `alert` mengembalikan `undefined`, jadi OR jalan ke operand kedua mencari nilai truthy.\n3. Operand kedua `2` truthy, jadi eksekusinya disela, `2` dikembalikan dan ditampilkan oleh alert terluar.\n\nTak akan ada `3`, karena evaluasinya tidak mencapai `alert(3)`.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/2-alert-or/task.md",
    "content": "niai penting: 3\n\n---\n\n# Apa hasil dari alert yang di-OR-kan?\n\nApa output kode di bawah?\n\n```js\nalert( alert(1) || 2 || alert(3) );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/solution.md",
    "content": "Jawabannya: `null`, karena `null` adalah nilai falsy pertama yang ada di daftar.\n\n```js run\nalert( 1 && null && 2 );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/3-alert-1-null-2/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Apa hasil AND?\n\nKode ini akan menampilkan apa?\n\n```js\nalert( 1 && null && 2 );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/4-alert-and/solution.md",
    "content": "Jawabannya: `1`, dan kemudian `undefined`.\n\n```js run\nalert( alert(1) && alert(2) );\n```\n\nPanggilan `alert` mengembalikan `undefined` (ia cuma menampilkan pesan, jadi tak ada kembalian berarti).\n\nKarena itu, `&&` mengevaluasi operand kiri (output `1`), dan langsung berhenti, karena `undefined` adalah nilai falsy. \nDan `&&` mencari nilai falsy dan mengembalikannya, jadi begitulah.\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/4-alert-and/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Apa hasil dari alert yang di-AND-kan?\n\nKode ini akan menampilkan apa?\n\n```js\nalert( alert(1) && alert(2) );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/5-alert-and-or/solution.md",
    "content": "Jawabannya: `3`.\n\n```js run\nalert( null || 2 && 3 || 4 );\n```\n\nPresedensi AND `&&` lebih tinggi dari `||`, jadi ia jalan pertama.\n\nHasil dari `2 && 3 = 3`, jadi expresinya menjadi:\n\n```\nnull || 3 || 4\n```\n\nSekarang hasilnya jadi nilai truthy pertama: `3`.\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/5-alert-and-or/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Hasil dari OR AND OR\n\nHasilnya akan jadi apa?\n\n```js\nalert( null || 2 && 3 || 4 );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/6-check-if-in-range/solution.md",
    "content": "\n\n```js\nif (age >= 14 && age <= 90)\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/6-check-if-in-range/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Cek kisaran antara\n\nTulis satu kondisi \"if\" untuk mengecek bahwa `age` ada di antara `14` dan `90` secara inklusif.\n\n\"Secara inklusif\" artinya bahwa `age` bisa mencapai `14` atau `90`.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/7-check-if-out-range/solution.md",
    "content": "Varian pertama:\n\n```js\nif (!(age >= 14 && age <= 90))\n```\n\nVarian kedua:\n\n```js\nif (age < 14 || age > 90)\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/7-check-if-out-range/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Cek kisaran luar\n\nTulis satu kondisi `if` untuk mengecek bahwa `age` BUKAN di antara 14 dan 90 secara inklusif.\n\nBuat dua varian: pertama menggunakan NOT `!`, kedua -- tanpaanya.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/8-if-question/solution.md",
    "content": "Jawabannya: pertama dan ketiga akan diexekusi.\n\nDetil:\n\n```js run\n// Berjalan.\n// Hasil dari -1 || 0 = -1, truthy\nif (-1 || 0) alert( 'first' );\n\n// Tidak berjalan\n// -1 && 0 = 0, falsy\nif (-1 && 0) alert( 'second' );\n\n// Eksekusi\n// Operator && mempunyai hak yang lebih tinggi daripada ||\n// jadi -1 && 1 dieksekusi pertama, dan memberikan rentetan:\n// null || -1 && 1  ->  null || 1  ->  1\nif (null || -1 && 1) alert( 'third' );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/8-if-question/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Pertanyaan tentang \"if\"\n\nMana dari `alert` berikut yang akan diexekusi?\n\nHasil expresinya akan jadi seperti apa di dalam `if(...)`?\n\n```js\nif (-1 || 0) alert( 'first' );\nif (-1 && 0) alert( 'second' );\nif (null || -1 && 1) alert( 'third' );\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/9-check-login/solution.md",
    "content": "\n\n```js run demo\nlet userName = prompt(\"Who's there?\", '');\n\nif (userName === 'Admin') {\n\n  let pass = prompt('Password?', '');\n\n  if (pass === 'TheMaster') {\n    alert( 'Welcome!' );\n  } else if (pass === '' || pass === null) {\n    alert( 'Canceled' );\n  } else {\n    alert( 'Wrong password' );\n  }\n\n} else if (userName === '' || userName === null) {\n  alert( 'Canceled' );\n} else {\n  alert( \"I don't know you\" );\n}\n```\n\nPerhatikan indent vertkal di dalam blok `if`. Mereka secara teknis tak dibutuhkan, tapi membuat kode lebih mudah dibaca.\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/9-check-login/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Cek login\n\nTulis kode yang meminta login dengan `prompt`.\n\nJika pengunjung menekan `\"Admin\"`, maka `prompt` untuk katasandi, jika inputannya beruba baris kosong atau `key:Esc` -- tampilkan \"Canceled.\", jika string lain -- maka tampilkan \"I don't know you\".\n\nKatasandinya dicek sebagai berikut:\n\n- Jika ia sama dengan \"TheMaster\", maka tampilkan \"Welcome!\",\n- String lain -- tampilkan \"Wrong password\",\n- Untuk string kosong atau batal input, tampilkan \"Canceled.\"\n\nSkemanya:\n\n![](ifelse_task.svg)\n\nSilakan gunakan blok `if` bersarang. Abaikan kemudahan-baca seluruh kode.\n\nPetunjuk:  mengoper inputan kosong ke prompt mengembalikan string kosong `''`. Menekan `key:ESC` saat prompt mengembalikan `null`.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/11-logical-operators/article.md",
    "content": "# Operator logika\n\nAda tiga operator logika di JavaScript: `||` (OR), `&&` (AND), `!` (NOT).\n\nMeski mereka dipanggil \"logika\", mereka bisa diaplikasikan ke nilai tipe apapun, bukan cuma boolean. Hasil mereka bisa juga tipe apapun.\n\nMari kita lihat detilnya.\n\n## || (OR)\n\nOperator \"OR\" diwakili dengan dua simbol garis vertical:\n\n```js\nresult = a || b;\n```\n\nDi pemrograman klasik, logika OR gunanya cuma untuk memanipulasi nilai boolean. Jika argumennya ada yang `true`, ia mengembalikan `true`, tapi jika tidak, maka ia mengembalikan `false`.\n\nDi JavaScript, operator ini agak tricky dan lebih kuat. Tapi pertama-tama, ayo kita lihat apa yang terjadi pada nilai boolean.\n\nAda empat kemungkinan kombinasi logika:\n\n```js run\nalert( true || true );   // true\nalert( false || true );  // true\nalert( true || false );  // true\nalert( false || false ); // false\n```\n\nSeperti yang kita lihat, hasilnya selalu `true` kecuali jika kedua operand sama-sama `false`.\n\nJika operand bukan boolean, ia dikonversi ke boolean untuk evaluasi.\n\nMisalnya, angka `1` diperlakukan sebagai `true`, angka `0` sebagai `false`:\n\n```js run\nif (1 || 0) { // bekerja seperti if( true || false )\n  alert( 'truthy!' );\n}\n```\n\nSeringkali, OR `||` digunakan di pernyataan `if` untuk menguji apakah ada satu kondisi *manapun* yang `true`.\n\nMisalnya:\n\n```js run\nlet hour = 9;\n\n*!*\nif (hour < 10 || hour > 18) {\n*/!*\n  alert( 'The office is closed.' );\n}\n```\n\nKita bisa menyampaikan kondisi lebih:\n\n```js run\nlet hour = 12;\nlet isWeekend = true;\n\nif (hour < 10 || hour > 18 || isWeekend) {\n  alert( 'The office is closed.' ); // akhir minggu\n}\n```\n\n## OR \"||\" mencari nilai benar pertama [#or-finds-the-first-truthy-value]\n\nLogika di atas memang klasik. Sekarang, mari bawa fitur \"extra\" JavaScript.\n\nAlgoritma luas bekerja seperti berikut.\n\nUntuk nilai yang diORkan:\n\n```js\nresult = value1 || value2 || value3;\n```\n\nOperator OR `||` melakukan hal berikut:\n\n- Mengevaluasi operand dari kiri ke kanan.\n- Untuk tiap operand, konversikan ia ke boolean. Jika hasilnya `true`, stop dan mengembalikan nilai original dari operand.\n- Jika semua operand telah dievaluasi (misal semuanya `false`), mengembalikan operand terakhir.\n\nNilai dikembalikan di bentuk originalnya, tanpa konversi.\n\nDengan kata lain, rantai OR `\"||\"` mengembalikan nilai truthy pertama atau yang terakhir jika tak ada nilai benar.\n\nMisalnya:\n\n```js run\nalert( 1 || 0 ); // 1 (1 truthy)\nalert( true || 'no matter what' ); // (true ialah truthy)\n\nalert( null || 1 ); // 1 (1 ialah nilai truthy pertama)\nalert( null || 0 || 1 ); // 1 (nilai truthy pertama)\nalert( undefined || null || 0 ); // 0 (semua falsy, mengembalikan nilai terakhir)\n```\n\nHal ini menjadikan penggunaan yang menarik dibanding \"OR booleanpure, classical, boolean-only OR\".\n\n1. **Dapatkan nilai truthy dari daftar variabel atau expresi.**\n\n    Untuk contoh, kita punya variabel `firstName`, `lastName` dan `nickName`, semuanya bersifat opsional.\n\n    Kita gunakan OR `||` untuk memilih satu-satunya yang memiliki data dan menampilkannya (atau `anonymous` jika belum ada yang ditentukan atau di set):\n\n    ```js run\n    let firstName = \"\";\n    let lastName = \"\";\n    let nickName = \"SuperCoder\";\n\n    *!*\n    alert( firstName || lastName || nickName || \"Anonymous\"); // SuperCoder\n    */!*\n    ```\n\n    Jika semua variabel bernilai falsy, `Anonymous` akan muncul.\n\n2. **Evaluasi Short-circuit.**\n\n    Fitur lainnya dari operator OR `||` adalah evaluasi \"short-circuit\".\n\n    Itu berarti bahwa `||` memproses argumennya sampai nilai pertama bersifat truthy tercapai, lalu nilainya dikembalikan langsung, bahkan tanpa menyentuh argumen lainnya.\n\n    Pentingnya dari fitur ini menjadi jelas jika sebuah operan bukan hanya sebuah nilai, tapi sebuah ekspresi yang melakukan aksi, seperti assignment sebuah variabel atau sebuah pemanggilan fungsi.\n\n    Didalam contoh dibawah, hanya pesan kedua yang di jalankan:\n\n    ```js run no-beautify\n    *!*true*/!* || alert(\"not printed\");\n    *!*false*/!* || alert(\"printed\");\n    ```\n\n    Di baris pertama, operator OR `||` langsung berhenti mengevaluasi karena nilai pertama bersifat `true`, jadi `alert`nya tidak berjalan.\n\n    Terkadang, orang-orang menggunakan fitur ini untuk mengeksekusi perintah hanya jika kondisi di paling kiri bersifat falsy.\n\n## && (AND)\n\nOperator AND diwakili dua ampersand `&&`:\n\n```js\nresult = a && b;\n```\n\nDalam pemrograman klasik, AND mengembalikan `true` jika kedua operand sama-sama truthy dan `false` jika sebaliknya:\n\n```js run\nalert( true && true );   // true\nalert( false && true );  // false\nalert( true && false );  // false\nalert( false && false ); // false\n```\n\nContoh dengan `if`:\n\n```js run\nlet hour = 12;\nlet minute = 30;\n\nif (hour == 12 && minute == 30) {\n  alert( 'The time is 12:30' );\n}\n```\n\nSama seperti OR, nilai apapun boleh menjadi operand dari AND:\n\n```js run\nif (1 && 0) { // dievaluasi sebagai true && false\n  alert( \"won't work, because the result is falsy\" );\n}\n```\n\n\n## AND \"&&\" mencari nilai falsy pertama\n\nMisal ada beberapa nilai di-AND-kan:\n\n```js\nresult = value1 && value2 && value3;\n```\n\nYang dilakukan operator AND `&&` adalah sebagai berikut:\n\n- Mengevaluasi operand dari kiri ke kanan.\n- Untuk tiap operand, konversi ia ke boolean. Jika hasilnya `false`, stop dan kembalikan nilai original operand tersebut.\n- Jika semua operand dievaluasi (i.e. semua truthy), mengembalikan operand terakhir.\n\nDengan kata lain, AND mengembalikan nilai falsy pertama atau nilai terakhir jika tak ketemu satupun nilai falsy.\n\nAturan di atas mirip dengan OR. Bedanya ialah AND mengembalikan niai *falsy* pertama sedangkan OR mengembalikan nilai *truthy* pertama.\n\nMisalnya:\n\n```js run\n// jika operand pertama truthy,\n// AND mengembalikan operand kedua:\nalert( 1 && 0 ); // 0\nalert( 1 && 5 ); // 5\n\n// jika operand pertama falsy,\n// AND mengembalikan itu. Operand kedua diabaikan\nalert( null && 5 ); // null\nalert( 0 && \"no matter what\" ); // 0\n```\n\nKita juga bisa mengoper beberapa nilai dalam satu barus. Lihat bagaimana nilai falsy pertama dikembalikan:\n\n```js run\nalert( 1 && 2 && null && 3 ); // null\n```\n\nKetika semua nilai truthy, nilai terakhir dikembalikan:\n\n```js run\nalert( 1 && 2 && 3 ); // 3, the last one\n```\n\n````smart header=\"Precedence of AND `&&` is higher than OR `||`\"\nPresedensi operator AND `&&` lebih tinggi dari OR `||`.\n\nJadi kode `a && b || c && d` esensinya sama dengan jika expresi `&&` dibungkus tanda kurung: `(a && b) || (c && d)`.\n````\n\n\n````warn header=\"Jangan ganti `if` dengan || atau &&\"\nTerkadang, orang-orang menggunakan operator AND `&&` untuk \"memperpendek instruksi `if`\".\n\n\nMisalnya:\n\n```js run\nlet x = 1;\n\n(x > 0) && alert( 'Greater than zero!' );\n```\n\nAksi di bagian kanan `&&` akan diexekusi hanya jika evaluasinya mencapai itu. Yaitu, hanya jika `(x > 0)` true.\n\nJadi pada dasarnya kita punya analogi untuk:\n\n```js run\nlet x = 1;\n\nif (x > 0) alert( 'Greater than zero!' );\n```\n\nWalaupun, versi dengan `&&` muncul lebih pendek, `if` menjadi jelas dan sedikit lebih mudah dibaca. Jadi kita merekomendasikan menggunakannya untuk setiap kebutuhan: gunakan `if` jika kita ingin if dan gunakan `&&` jika kita ingin AND.\n````\n\n\n## ! (NOT)\n\nOperator boolean NOT diwakili dengan tanda exklamasi `!`.\n\nSyntaxnya cukup simpel:\n\n```js\nresult = !value;\n```\n\nOperator ini menerima argumen tunggal dan menjalankan hal berikut:\n\n1. Mengkonversi operand ke tipe boolean: `true/false`.\n2. Mengembalikan nilai kebalikan.\n\nMisalnya:\n\n```js run\nalert( !true ); // false\nalert( !0 ); // true\n```\n\nNOT ganda `!!` kadang dipakai untuk mengkonversi nilai ke tipe boolean:\n\n```js run\nalert( !!\"non-empty string\" ); // true\nalert( !!null ); // false\n```\n\nYaitu, NOT pertama mengkonversi nilai ke boolean dan mengembalikan kebalikannya, dan NOT kedua membaliknya lagi. Ujungnya, kita punya konversi nilai-ke-boolean biasa.\n\nAda sedikit cara rewel untuk melakukan hal serupa -- fungsi `Boolean` built-in:\n\n```js run\nalert( Boolean(\"non-empty string\") ); // true\nalert( Boolean(null) ); // false\n```\n\nPresedensi NOT `!` paling tinggi dari semua operator logika, jadi ia selalu jalan pertama, sebelum `&&` or `||`.\n"
  },
  {
    "path": "1-js/02-first-steps/12-nullish-coalescing-operator/article.md",
    "content": "# Operator penggabungan nullish '??'\n\n[browser terbaru=\"baru\"]\n\nOperator penggabungan nullish ditulis sebagai dua tanda tanya `??`.\n\nKarena memperlakukan `null` dan `undefined` sama, kita akan menggunakan istilah khusus di sini, di artikel ini. Kami akan mengatakan bahwa ekspresi adalah \"didefinisikan\" ketika itu bukan `null` atau `undefined`.\n\nHasil dari `a?? b` adalah:\n- jika `a` didefinisikan, maka `a`,\n- jika `a` tidak didefinisikan, maka `b`.\n\nDengan kata lain, `??` mengembalikan argumen pertama jika bukan `null/undefined`. Kalau tidak, yang kedua.\n\nOperator penggabungan nullish bukanlah sesuatu yang benar-benar baru. Ini hanya sintaks yang bagus untuk mendapatkan nilai \"didefinisikan\" pertama dari keduanya.\n\nKita dapat menulis ulang `result = a ?? b` menggunakan operator yang sudah kita kenal, seperti ini:\n\n```js\nresult = (a !== null && a !== undefined) ? a : b;\n```\n\nSekarang harus benar-benar jelas apa yang dilakukan `??`. Mari kita lihat di mana itu membantu.\n\nKasus penggunaan umum untuk `??` adalah memberikan nilai default untuk variabel yang berpotensi tidak terdefinisi.\n\nMisalnya, di sini kami menampilkan `pengguna` jika didefinisikan, jika tidak `Anonim`:\n\n```js dijalankan\nbiarkan pengguna;\n\nalert(pengguna ?? \"Anonim\"); // Anonim (pengguna tidak ditentukan)\n```\n\nBerikut ini contoh dengan `pengguna` ditetapkan ke sebuah nama:\n\n```js dijalankan\nbiarkan pengguna = \"John\";\n\nalert(pengguna ?? \"Anonim\"); // John (ditentukan pengguna)\n```\n\nKita juga dapat menggunakan urutan `??` untuk memilih nilai pertama dari daftar yang bukan `null/undefined`.\n\nKatakanlah kita memiliki data pengguna dalam variabel `FirstName`, `lastName` atau `nickName`. Semuanya mungkin tidak ditentukan, jika pengguna memutuskan untuk tidak memasukkan nilai.\n\nKami ingin menampilkan nama pengguna menggunakan salah satu variabel ini, atau menampilkan \"Anonim\" jika semuanya tidak ditentukan.\n\nMari kita gunakan operator `??` untuk itu:\n\n```js dijalankan\nbiarkan namadepan = null;\nbiarkan namabelakang = null;\nbiarkan nickName = \"Supercoder\";\n\n// menunjukkan nilai yang ditentukan pertama:\n*!*\nalert(FirstName ?? LastName ?? nickName ?? \"Anonim\"); // kode super\n*/!*\n```\n\n## Perbandingan dengan ||\n\nOperator OR `||` dapat digunakan dengan cara yang sama seperti `??`, seperti yang dijelaskan di [bab sebelumnya](info:logical-operators#or-finds-the-first-truthy-value).\n\nMisalnya, pada kode di atas kita dapat mengganti `??` dengan `||` dan tetap mendapatkan hasil yang sama:\n\n```js dijalankan\nbiarkan namadepan = null;\nbiarkan namabelakang = null;\nbiarkan nickName = \"Supercoder\";\n\n// menunjukkan nilai kebenaran pertama:\n*!*\nalert(Namadepan || Nama Belakang || Nama Panggilan || \"Anonim\"); // kode super\n*/!*\n```\n\nSecara historis, operator OR `||` ada terlebih dahulu. Itu ada sejak awal JavaScript, jadi pengembang menggunakannya untuk tujuan seperti itu untuk waktu yang lama.\n\nDi sisi lain, operator penggabungan nullish `??` baru saja ditambahkan ke JavaScript, dan alasannya adalah karena orang-orang tidak terlalu senang dengan `||`.\n\nPerbedaan penting di antara mereka adalah bahwa:\n- `||` mengembalikan nilai *truth* pertama.\n- `??` mengembalikan nilai *yang ditentukan* pertama.\n\nDengan kata lain, `||` tidak membedakan antara `false`, `0`, string kosong `\"\"` dan `null/undefined`. Semuanya sama -- nilai yang salah. Jika salah satu dari ini adalah argumen pertama dari `||`, maka kita akan mendapatkan argumen kedua sebagai hasilnya.\n\nNamun dalam praktiknya, kita mungkin ingin menggunakan nilai default hanya jika variabelnya `null/undefined`. Artinya, ketika nilainya benar-benar tidak diketahui/tidak disetel.\n\nMisalnya, pertimbangkan ini:\n\n```js dijalankan\nmisalkan tinggi = 0;\n\nwaspada(tinggi || 100); // 100\nwaspada (tinggi ?? 100); // 0\n```\n\n- `tinggi || 100` memeriksa `height` sebagai nilai yang salah, dan itu adalah `0`, memang salah.\n    - jadi hasil dari `||` adalah argumen kedua, `100`.\n- `tinggi ?? 100` memeriksa `height` sebagai `null/undefined`, dan bukan,\n    - jadi hasilnya adalah `height` \"sebagaimana adanya\", yaitu `0`.\n\nDalam praktiknya, ketinggian nol seringkali merupakan nilai yang valid, yang tidak boleh diganti dengan default. Jadi `??` melakukan hal yang benar.\n\n## Prioritas\n\nPrioritas operator `??` hampir sama dengan `||`, hanya sedikit lebih rendah. Ini sama dengan `5` di [tabel MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence#Table), sedangkan `||` adalah `6` .\n\nArtinya, seperti halnya `||`, operator penggabungan nullish `??` dievaluasi sebelum `=` dan `?`, tetapi setelah sebagian besar operasi lain, seperti `+`, `*`.\n\nJadi, jika kita ingin memilih nilai dengan `??` dalam ekspresi dengan operator lain, pertimbangkan untuk menambahkan tanda kurung:\n\n```js dijalankan\nbiarkan tinggi = nol;\nbiarkan lebar = nol;\n\n// penting: gunakan tanda kurung\nbiarkan luas = (tinggi ?? 100) * (lebar ?? 50);\n\nwaspada (daerah); // 5000\n```\n\nJika tidak, jika kita menghilangkan tanda kurung, maka karena `*` memiliki prioritas yang lebih tinggi daripada `??`, tanda kurung akan dieksekusi terlebih dahulu, sehingga menghasilkan hasil yang salah.\n\n```js\n// tanpa tanda kurung\nmisal luas = tinggi?? 100 * lebar ?? 50;\n\n// ...berfungsi sama seperti ini (mungkin bukan yang kita inginkan):\nmisal luas = tinggi?? (100 * lebar) ?? 50;\n```\n\n### Menggunakan ?? dengan && atau ||\n\nKarena alasan keamanan, JavaScript melarang penggunaan `??` bersama dengan operator `&&` dan `||`, kecuali jika prioritas secara eksplisit ditentukan dengan tanda kurung.\n\nKode di bawah ini memicu kesalahan sintaks:\n\n```js dijalankan\nmisalkan x = 1 && 2 ?? 3; // Kesalahan sintaks\n```\n\nBatasan ini tentu masih bisa diperdebatkan, hal itu ditambahkan ke spesifikasi bahasa dengan tujuan untuk menghindari kesalahan pemrograman, ketika orang mulai beralih dari `||` ke `??`.\n\nGunakan tanda kurung eksplisit untuk mengatasinya:\n\n```js dijalankan\n*!*\nmisalkan x = (1 && 2) ?? 3; // Bekerja\n*/!*\n\nwaspada(x); // 2\n```\n\n## Ringkasan\n\n- Operator penggabungan nullish `??` menyediakan cara singkat untuk memilih nilai \"ditentukan\" pertama dari daftar.\n\n    Ini digunakan untuk menetapkan nilai default ke variabel:\n\n    ```js\n    // atur tinggi=100, jika tinggi nol atau tidak terdefinisi\n    tinggi = tinggi?? 100;\n    ```\n\n- Operator `??` memiliki prioritas yang sangat rendah, hanya sedikit lebih tinggi dari `?` dan `=`, jadi pertimbangkan untuk menambahkan tanda kurung saat menggunakannya dalam ekspresi.\n- Dilarang menggunakannya dengan `||` atau `&&` tanpa tanda kurung eksplisit."
  },
  {
    "path": "1-js/02-first-steps/13-while-for/1-loop-last-value/solution.md",
    "content": "Jawabannya: `1`.\n\n```js run\nlet i = 3;\n\nwhile (i) {\n  alert( i-- );\n}\n```\n\nSetiap pengulangan mengurangi `i`  dengan `1`. pengecekan `while(i)` menghentikan perulangan ketika `i = 0`.\n\nOleh karena itu, langkah-langkah perulangan membentuk urutan sebagai berikut (\"loop unrolled\"):\n\n```js\nlet i = 3;\n\nalert(i--); // menampilkan 3, mengurangi i menjadi 2\n\nalert(i--) // menampilkan 2, mengurangi i menjadi 1\n\nalert(i--) // menampilkan 1, mengurangi i menjadi 0\n\n// selesai, pengecekan while(i) menghentikan pengulangan\n```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/1-loop-last-value/task.md",
    "content": "importance: 3\n\n---\n\n# Nilai terakhir perulangan\n\nApa nilai terakhir yang diperingatkan oleh kode ini? Mengapa?\n\n```js\nlet i = 3;\n\nwhile (i) {\n  alert( i-- );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/2-which-value-while/solution.md",
    "content": "Tugas mendemonstrasikan bagaimana bentuk postfix/prefix dapat menyebabkan hasil yang berbeda ketika digunakan dalam perbandingan\n\n1. **Dari 1 ke 4**\n\n    ```js run\n    let i = 0;\n    while (++i < 5) alert( i );\n    ```\n\n    nilai pertama adalah `i = 1`, karena `++i` menambah terlebih dahulu `i` dan mengembalikan nilai baru. Jadi perbandingan pertama adalah `1 < 5` dan `alert` menampilkan `1`.\n\n    lalu diikuti `2, 3, 4…` -- nilainya muncul satu per satu. Perbandingan selalu menggunakan nilai yang ditambah, karna ada `++` sebelum variabel.\n\n    Akhirnya, `i = 4` bertambah menjadi `5`, perbandingan `while(5 < 5)` gagal, dan pengulangan berhenti. Jadi `5` tidak ditampilkan.\n2. **Dari 1 ke 5**\n\n    ```js run\n    let i = 0;\n    while (i++ < 5) alert( i );\n    ```\n\n    Lagi, nilai pertama adalah `i = 1`. bentuk postfix dari `i++` menambah `i` dan kemudian mengembalikan nilai yang *lama*, jadi perbandingan `i++ < 5` akan menggunakan `i = 0` (berbeda dengan `++i < 5`).\n\n    Namun panggilan `alert` terpisah. ini adalah pernyataan lain yang berjalan setelah pertambahan dan perbandingan. Jadi ini mendapatkan nilai yang saat ini `i = 1`.\n\n    Lalu diikuti `2, 3, 4…`\n\n    Mari berhenti di `i = 4`. bentuk prefix `++i` akan menaikannya dan menggunakan `5` di perbandingan. Tapi disini kita mempunyai bentuk postfix `i++`. jadi ini menambah `i` menjadi `5`, namun mengembalikan nilai yang lama. Karna perbandingan yang sebenarnya adalah `while(4 < 5)` -- benar, dan kontrol berlanjut ke `alert`.\n\n    Nilai `i = 5` adalah yang terkahir, karena pada langkah berikutnya `while(5 < 5)` adalah salah.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/2-which-value-while/task.md",
    "content": "importance: 4\n\n---\n\n# Nilai mana yang ditampilkan perulangan while?\n\nUntuk setiap iterasi, tulis nilai mana yang dikeluarkan dan bandingkan dengan solusinya.\n\nKedua perulangan `alert` nilai yang sama, atau tidak?\n\n1. Bentuk prefix `++i`:\n\n    ```js\n    let i = 0;\n    while (++i < 5) alert( i );\n    ```\n2. Bentuk postfix `i++`\n\n    ```js\n    let i = 0;\n    while (i++ < 5) alert( i );\n    ```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/3-which-value-for/solution.md",
    "content": "**Jawabanya: dari `0` ke `4` pada kedua kasus.**\n\n```js run\nfor (let i = 0; i < 5; ++i) alert( i );\n\nfor (let i = 0; i < 5; i++) alert( i );\n```\n\nItu dapat dengan mudah dikurangkan dari algoritma `for`:\n\n1. Jalankan sekali `i = 0` sebelum apapun (begin).\n2. Cek kondisinya `i < 5`\n3. Jika `true` -- jalankan loop body `alert(i)`, dan kemudian `i++`\n\npertambahan `i++` terpisah dari pengecekan kondisi (2). itu hanya pernyataan lain.\n\nNilai yang dikembalikan oleh pertambahan tidak digunakan disini, jadi tidak ada bedanya antara `i++` dan `++i`.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/3-which-value-for/task.md",
    "content": "importance: 4\n\n---\n\n# Nilai mana yang ditampilkan oleh perulangan \"for\" ?\n\nUntuk setiap perulangan tulis nilai mana yang akan ditampilkan. lalu bandingkan dengan jawabanya\n\nKedua perulangan `alert` nilai yang sama atau tidak?\n\n1. Bentuk postfix:\n\n    ```js\n    for (let i = 0; i < 5; i++) alert( i );\n    ```\n2. Bentuk prefix:\n\n    ```js\n    for (let i = 0; i < 5; ++i) alert( i );\n    ```\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/4-for-even/solution.md",
    "content": "\n\n```js run demo\nfor (let i = 2; i <= 10; i++) {\n  if (i % 2 == 0) {\n    alert( i );\n  }\n}\n```\n\nKita menggunakan operator \"modulo\" `%` untuk mendapatkan nilai sisa dan untuk mengecek kegenapan disini.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/4-for-even/task.md",
    "content": "importance: 5\n\n---\n\n# Menghasilkan angka genap di perulangan\n\nGunakan perulangan `for` untuk menghasilkan angka genap dari `2`sampai `10`.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/5-replace-for-while/solution.md",
    "content": "\n\n```js run\nlet i = 0;\nwhile (i < 3) {\n  alert( `number ${i}!` );\n  i++;\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/5-replace-for-while/task.md",
    "content": "importance: 5\n\n---\n\n# Ganti \"for\" dengan \"while\"\n\nTulis ulang kode ubah perulangan `for` ke `while` tanpa mengubah perilakunya (hasilnya harus tetap sama).\n\n```js run\nfor (let i = 0; i < 3; i++) {\n  alert( `number ${i}!` );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/6-repeat-until-correct/solution.md",
    "content": "\n```js run demo\nlet num;\n\ndo {\n  num = prompt(\"Masukan angka lebih dari 100?\", 0);\n} while (num <= 100 && num);\n```\n\nPerulangan `do..while` diulangi selagi kedua cek itu bernilai benar:\n\n1. Pengecekan untuk `num <= 100` -- itu adalah, nilai yang dimasukan masih tidak lebih besar dari `100`.\n2. Pengecekan `&& num` adalah salah ketika `num` adalah `null` atau sebuah string kosong. kemudian perulangan `while` berhenti juga.\n\nP.S. Jika `num` adalah `null` lalu `num <= 100` adalah `true`, jadi tanpa pengecekan kedua, perulangan tidak akan berhenti jika pengguna mengeclick CANCEL. Kedua pengecekan dibutuhkan.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/6-repeat-until-correct/task.md",
    "content": "importance: 5\n\n---\n\n# Ulangi sampai inputnya benar\n\nTulis sebuah perulangan yang meminta angka lebih dari `100`. Jika pendatang memasukan angka lainnya -- tanya mereka untuk menginput kembali.\n\nPerulangan harus menanyakan untuk sebuah angka sampai pengunjung memasukan angka lebih dari `100` atau membatalkan input/memasukan sebuah baris kosong.\n\nDisini kita dapat menganggap bahwa pengunjung hanya menginput angka. Tidak perlu menerapkan penanganan spesial untuk input non-numerik di tugas ini.\n\n[demo]\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/7-list-primes/solution.md",
    "content": "Ada banyak algoritma untuk tugas ini.\n\nMari gunakan perulangan bersarang:\n\n```js\nFor each i in the interval {\n  cek if i has a divisor from 1..i\n  if yes => the value is not a prime\n  if no => the value is a prime, show it\n}\n```\n\nKode menggunakan label:\n\n```js run\nlet n = 10;\n\nnextPrime:\nfor (let i = 2; i <= n; i++) { // untuk setiap i...\n\n  for (let j = 2; j < i; j++) { // mencari pembagi..\n    if (i % j == 0) continue nextPrime; // bukan prima, pergi ke i berikutnya\n  }\n\n  alert( i ); // prima\n}\n```\n\nAda banyak ruang untuk mengoptimalkannya. Misalnya, kita dapat mencari pembagi dari `2` ke akar kuadrat dari `i`. Tapi bagaimanapun, jika kita ingin sangat efisien untuk interval besar, kita perlu mengubah pendekatan dan mengandalkan matematika lanjutan dan algotima rumit seperti [Quadratic sieve](https://en.wikipedia.org/wiki/Quadratic_sieve), [General number field sieve](https://en.wikipedia.org/wiki/General_number_field_sieve) dsb.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/7-list-primes/task.md",
    "content": "importance: 3\n\n---\n\n# Menghasilkan bilangan prima\n\nAngka integer lebih dari `1` disebut [bilangan prima](https://en.wikipedia.org/wiki/Prime_number) jika itu tidak bisa dibagi tanpa sisa oleh siapapun kecuali `1` dan bilangan itu sendiri.\n\nDengan kata lain, `n > 1` adalah prima jika tidak dapat dibagi secara merata oleh apapun kecuali `1` dan `n`.\n\nContohnya, `5` adalah prima, karna tidak bisa dibagi tanpa sisa oleh `2`, `3` dan `4`.\n\n**Tulis kode yang menghasilkan bilangan prima dalam interval dari `2` sampai `n`.**\n\nUntuk `n = 10` hasilnya akan `2,3,5,7`.\n\nP.S. Kode harus bekerja untuk segala `n`, tidak disetel untuk nilai tetap apapun.\n"
  },
  {
    "path": "1-js/02-first-steps/13-while-for/article.md",
    "content": "# Perulangan: while dan for\n\nKita sering perlu untuk mengulangi tindakan.\n\nContohnya, Mengeluarkan barang dari sebuah daftar satu per satu atau hanya menjalankan kode yang sama untuk setiap nomor dari 1 hingga 10.\n\n*Perulangan* adalah sebuah cara untuk mengulangi kode yang sama beberapa kali.\n\n## Perulangan \"while\"\n\nPerulangan `while` memiliki sintaks sebagai berikut:\n\n```js\nwhile (kondisi) {\n  // kode\n  // disebut \"badan perulangan\"\n}\n```\n\nKetika `kondisi` bernilai truthy, `kode` dari badan perulangan dijalankan.\n\nContohnya, perulangan di bawah mengeluarkan `i` selagi `i < 3`:\n\n```js run\nlet i = 0;\nwhile (i < 3) { // menampilkan 0, lalu 1, lalu 2\n  alert( i );\n  i++;\n}\n```\n\nEksekusi tunggal dari badan perulangan disebut *sebuah pengulangan*. Perulangan pada contoh diatas membuat tiga kali pengulangan.\n\nJika `i++` hilang dari contoh di atas, perulangan akan mengulangi (dalam teori) secara terus-menerus. Pada praktiknya, browser menyediakan cara untuk menghentikan perulangan, dan pada sisi server JavaScript, kita dapat mematikan prosesnya.\n\nEkspresi atau variable apapun bisa menjadi sebuah kondisi perulangan, tidak hanya perbandingan: kondisi terevalusasi dan terkonversi menjadi boolean oleh `while`.\n\nContohnya, cara cepat untuk menulis `while (i != 0)` adalah `while (i)`:\n\n```js run\nlet i = 3;\n*!*\nwhile (i) { // ketika i menjadi 0, kondisi bernilai salah, dan perulangan berhenti\n*/!*\n  alert( i );\n  i--;\n}\n```\n\n````smart header=\"Kurung kurawal tidak dibutuhkan untuk badan baris tunggal\"\nJika badan perulangan mempunyai sebuah pernyataan tunggal, kita dapat menghilangkan kurung kurawal `{…}`:\n\n```js run\nlet i = 3;\n*!*\nwhile (i) alert(i--);\n*/!*\n```\n````\n\n## Perulangan \"do..while\"\n\nPengecekan kondisi dapat dipindahkan *di bawah* badan perulangan menggunakan `do..while` sintaks:\n\n```js\ndo {\n  // badan perulangan\n} while (condition);\n```\n\nPerulangan akan mengeksekusi badan terlebih dahulu, lalu memeriksa kondisi, dan, selagi itu bernilai truthy, jalankan itu lagi dan lagi.\n\nContohnya:\n\n```js run\nlet i = 0;\ndo {\n  alert( i );\n  i++;\n} while (i < 3);\n```\n\nFormat penulisan ini hanya digunakan ketika kamu ingin badan dari perulangan tereksekusi **setidaknya sekali** Terlepas dari kondisi menjadi bernilai benar. Biasanya, format lain yang dipilih: `while(…) {…}`.\n\n## Perulangan \"for\"\n\nPerulangan `for` lebih complex, tapi merupakan perulangan yang paling umum digunakan.\n\nItu terlihat seperti ini:\n\n```js\nfor (awal; kondisi; langkah) {\n  // ... badan perulangan ...\n}\n```\n\nMari belajar makna dari bagian ini dari contoh. Perulangan dibawah menjalankan `alert(i)` untuk `i` dari `0` sampai dengan (tapi tidak termasuk) `3`:\n\n```js run\nfor (let i = 0; i < 3; i++) { // menampilkan 0, lalu 1, lalu 2\n  alert(i);\n}\n```\n\nMari bahas pernyataan `for` bagian demi bagian:\n\n| bagian  |          |                                                                            |\n|-------|----------|----------------------------------------------------------------------------|\n| begin | `i = 0`    | Jalankan sekali masuk ke loop.                                      |\n| condition | `i < 3`| Cek sebelum tiap iterasi loop. Jika salah, loop berhenti.              |\n| body | `alert(i)`| Jalankan lagi dan lagi selama kondisi bernilai truthy.                         |\n| step | `i++`      | Exekusi setelah badan di tiap iterasi. |\n\nCara kerja algoritma perulangan umum seperti ini:\n\n```\nJalankan begin\n→ (jika condition → jalankan body dan jalankan step)\n→ (jika condition → jalankan body dan jalankan step)\n→ (jika condition → jalankan body dan jalankan step)\n→ ...\n```\n\nDikatakan, `begin` diexekusi sekali, kemudian ia beriterasi: setelah tiap `condition` dites, `body` dan `step` diexekusi.\n\nJika kamu baru pada perulangan, ini bisa membantumu kembali ke contoh dan mereproduksi bagamana ini berjalan selangkah demi selangkah pada sebuah selembar kertas.\n\nInilah yang sebenarnya terjadi pada kasus kita:\n\n```js\n// for (let i = 0; i < 3; i++) alert(i)\n\n// jalankan awal\nlet i = 0\n// jika kondisi → jalankan badan dan jalankan langkah\nif (i < 3) { alert(i); i++ }\n// jika kondisi → jalankan badan dan jalankan langkah\nif (i < 3) { alert(i); i++ }\n// jika kondisi → jalankan badan dan jalankan langkah\nif (i < 3) { alert(i); i++ }\n// ...selesai, karena sekarang i == 3\n```\n\n````smart header=\"Deklarasi varibel sebaris\"\nDisini, \"penghitung\" variabel `i` dideklarasikan di dalam perulangan. Ini disebut deklarasi variabel \"sebaris\". variabel ini hanya akan terlihat di dalam perulangan.\n\n```js run\nfor (*!*let*/!* i = 0; i < 3; i++) {\n  alert(i); // 0, 1, 2\n}\nalert(i); // error, tidak ada variabel\n```\n\nDaripada mendefinisikan sebuah variabel, kita dapat menggunakan yang sudah ada:\n\n```js run\nlet i = 0;\n\nfor (i = 0; i < 3; i++) { // gunakan variabel yang sudah ada\n  alert(i); // 0, 1, 2\n}\n\nalert(i); // 3, terlihat, karena dideklarasikan diluar dari perulangan\n```\n\n````\n\n\n### Melewatkan bagian\n\nBagian apapun dari `for` dapat dilewati.\n\nContoh, kita dapat menghilangkan `awal` jika kita tidak butuh untuk melakukan apapun pada awal perulangan.\n\nSeperti ini:\n\n```js run\nlet i = 0; // kita punya i yang sudah dideklarasikan dan telah ditetapkan\n\nfor (; i < 3; i++) { // tidak butuh \"awal\"\n  alert( i ); // 0, 1, 2\n}\n```\n\nKita juga bisa menghilangkan bagian `langkah`:\n\n```js run\nlet i = 0;\n\nfor (; i < 3;) {\n  alert( i++ );\n}\n```\n\nIni membuat perulangan sama dengan `while (i < 3)`.\n\nKita sebenarnya dapat menghilangkan semuanya, membuat sebuah perulangan tak terhingga:\n\n```js\nfor (;;) {\n  // ulangi tanpa batas\n}\n```\n\nTolong dicatat bahwa dua `for` titik koma `;` harus ada, jika tidak, akan ada sintaks error.\n\n## Menghentikan perulangan\n\nBiasanya, sebuah perulangan keluar ketika kondisinya menjadi bernilai salah.\n\nTapi kita dapat memaksanya keluar pada waktu apapun menggunakan perintah spesial `break`.\n\nContohnya, perulangan dibawah menanyakan pengguna untuk serangkaian angka, \"hentikan\" ketika tidak ada angka yang dimasukan:\n\n```js run\nlet sum = 0;\n\nwhile (true) {\n\n  let value = +prompt(\"Masukan sebuah angka\", '');\n\n*!*\n  if (!value) break; // (*)\n*/!*\n\n  sum += value;\n\n}\nalert( 'Sum: ' + sum );\n```\n\nPerintah `break` teraktivasi pada baris `(*)` jika pengguna memasukan baris kosong atau membatalkan input. itu akan langsung berhenti, melewati kontrol ke baris pertama setelah perulangan. yang bernama, `alert`.\n\nKombinasi \"perulangan tak terhingga + `break` sesuai kebutuhan\" bagus untuk situasi dimana sebuah kondisi perulangan harus diperiksa tidak di awal atau akhir dari perulangan, tapi di pertengahan atau bahkan di beberapa tempat tubuhnya.\n\n## Lanjutkan ke perulangan berikutnya [#lanjutkan]\n\nPerintah `continue` adalah \"versi ringan\" dari `break`. Ini tidak mengentikan keseluruhan perulangan. sebagai gantinya, ini menghentikan perulangan saat ini dan memaksa perulangan untuk memulai yang baru (jika kondisi diperbolehkan).\n\nKita dapat menggunakan ini jika kita selesai dengan perulangan saat ini dan ingin pindah ke yang berikutnya.\n\nPerulangan dibawah menggunakan `continue` untuk hanya menampilkan nilai ganjil:\n\n```js run no-beautify\nfor (let i = 0; i < 10; i++) {\n\n  // jika benar, lewati bagian badan perulangan yang tersisa\n  *!*if (i % 2 == 0) continue;*/!*\n\n  alert(i); // 1, then 3, 5, 7, 9\n}\n```\n\nUntuk nilai genap dari `i`, perintah `continue` mengentikan menjalankan badan dan melewati kontrol ke perulangan `for` berikutnya (dengan nomor berikutnya). jadi `alert` hanya terpanggil untuk nilai ganjil.\n\n````smart header=\"Perintah `continue` membantu mengurangi penyarangan\"\nSebuah perulangan yang menampilkan nilai ganjil dapat terlihat seperti ini:\n\n```js run\nfor (let i = 0; i < 10; i++) {\n\n  if (i % 2) {\n    alert( i );\n  }\n\n}\n```\n\nDari sudut pandang teknis, ini identik dengan contoh diatas. Tentunya, kita dapat membungkus kode dalam sebuah blok `if` daripada menggunakan `continue`.\n\nTapi sebagai efeknya, hal ini akan menciptakan kode lebih dalam satu tingkat (pemanggilan `alert` didalam kurung kurawal). Jika kode didalam `if` lebih panjang beberapa baris, hal itu akan membuat tingkat keterbacaan kode menjadi berkurang.\n````\n\n````warn header=\"Tidak ada `break/continue` ke sisi kanan '?'\"\nHarap perhatikan bahwa sintaks yang membangun yang bukan ekspresi tidak dapat digunakan dengan operator ternary `?`. Khususnya, perintah seperti `break/continue` tidak diperbolehkan.\n\nMisalnya, jika kita mengambil kode ini:\n\n```js\nif (i > 5) {\n  alert(i);\n} else {\n  continue;\n}\n```\n\n...dan tulis ulang menggunakan sebuah tanda tanya:\n\n\n```js no-beautify\n(i > 5) ? alert(i) : *!*continue*/!*; // continue tidak diperbolehkan disini\n```\n\n...ia berhenti jalan: ada galat syntax:\n\nIni hanya alasan lain untuk tidak menggunakan operator tanda tanya `?` daripada `if`.\n````\n\n## Label untuk break/continue\n\nTerkadang kita perlu keluar dari beberapa perulangan bersarang sekaligus.\n\nMisalnya, dalam kode di bawah kita lakukan perulangan terhadap `i` dan `j`, meminta koordinat `(i, j)` dari `(0,0)` ke`(3,3)`:\n\n```js run no-beautify\nfor (let i = 0; i < 3; i++) {\n\n  for (let j = 0; j < 3; j++) {\n\n    let input = prompt(`Nilai pada koordinasi (${i},${j})`, '');\n\n    // bagaimana jika saya ingin keluar dari sini ke Done (dibawah)?\n  }\n}\n\nalert('Done!');\n```\n\nKita butuh cara untuk menghentikan proses jika pengguna membatalkan input.\n\n`break` biasa setelah `input` hanya akan menghentikan perulangan dalam. Itu tidak cukup--label, datang untuk menyelamatkan!\n\nLabel adalah sebuah pengidentifikasi dengan sebuah titik dua sebelum perulangan:\n```js\nlabelName: for (...) {\n  ...\n}\n```\n\nPernyataan `break <labelName>` di dalam loop di bawah menghentikan pada label:\n\n```js run no-beautify\n*!*outer:*/!* for (let i = 0; i < 3; i++) {\n\n  for (let j = 0; j < 3; j++) {\n\n    let input = prompt(`Value at coords (${i},${j})`, '');\n\n    // jika sebuah string kosong atau terbatalkan, lalu hentikan kedua perulangan\n    if (!input) *!*break outer*/!*; // (*)\n\n    // lakukan sesuatu dengan nilai...\n  }\n}\nalert('Done!');\n```\n\nPada kode diatas, `break outer` melihat keatas untuk label bernama `outer` dan menghentikan perulangan itu.\n\nJadi kontrol pergi langsung dari `(*)` ke `alert('Done!')`.\n\nKita juga dapat memindah label ke sebuah baris terpisah:\n\n```js no-beautify\nouter:\nfor (let i = 0; i < 3; i++) { ... }\n```\n\nPerintah `continue` dapat juga digunakan dengan sebuah label. pada kasus ini, eksekusi kode berpindah ke perulangan label berikutnya.\n\n````warn header=\"Label tidak mengizinkan \\\"lompat\\\" ke manapun\"\nLabel tidak mengizinkan kita untuk lompat ke sembarang tempat dalam kode.\n\nMisalnya, mustahil melakukan ini:\n```js\nbreak label;  // tidak lompak ke label di bawah\n\nlabel: for (...)\n```\n\nDirektif `break` harus berada di dalam blok kode. Secara teknis, setiap blok kode berlabel akan dilakukan, contoh.:\n```js\nlabel: {\n  // ...\n  break label; // berjalan\n  // ...\n}\n```\n\n...Meskipun, 99,9% dari waktu `break` yang digunakan adalah loop dalam, seperti yang telah kita lihat pada contoh di atas.\n\n`continue` hanya dimungkinkan dari dalam loop.\n````\n\n## Ringkasan\n\nKita membahas 3 jenis perulangan:\n\n- `while` -- Kondisi diperiksa sebelum setiap perulangan.\n- `do..while` -- Kondisi diperiksa setelah setiap perulangan.\n- `for (;;)` -- Kondisi diperiksa sebelum setiap perulangan, pengaturan tambahan tersedia.\n\nUntuk membuat sebuah perulangan \"tak terhinggaa\", biasanya konstruksi `while(true)` digunakan. Demikian sebuah perulangan, seperti yang lainnya, dapat berhenti dengan perintah `break`.\n\nJika kita tidak ingin melakukan apapun di perulangan saat ini dan ingin meneruskan ke yang berikutnya, kita dapat menggunakan perintah `continue`.\n\n`break/continue` mendukung label sebelum perulangan. Label adalah satu-satunya cara untuk `break/continue` menghindari loop bersarang untuk pergi ke luar\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/solution.md",
    "content": "Supaya fungsionalitas `switch` persis sama, `if` harus memakai pembandingan ketat `'==='`.\n\nTapi untuk string yang diberikan, `'=='` sederhana cukup bekerja juga.\n\n```js no-beautify\nif(browser == 'Edge') {\n  alert(\"You've got the Edge!\");\n} else if (browser == 'Chrome'\n || browser == 'Firefox'\n || browser == 'Safari'\n || browser == 'Opera') {\n  alert( 'Okay we support these browsers too' );\n} else {\n  alert( 'We hope that this page looks ok!' );\n}\n```\n\nTolong ingat: konstruksi `browser == 'Chrome' || browser == 'Firefox' …` dipecah menjadi beberapa baris untuk kemudahan keterbacaan.\n\nTapi konstruksi `switch` masih lebih bersih dan lebih deskriptif.\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/1-rewrite-switch-if-else/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tulis kembali \"switch\" menjadi \"if\"\n\nTulis kode menggunakan `if..else` yang akan berkorespon dengan `switch` berikut:\n\n```js\nswitch (browser) {\n  case 'Edge':\n    alert( \"You've got the Edge!\" );\n    break;\n\n  case 'Chrome':\n  case 'Firefox':\n  case 'Safari':\n  case 'Opera':\n    alert( 'Okay we support these browsers too' );\n    break;\n\n  default:\n    alert( 'We hope that this page looks ok!' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/2-rewrite-if-switch/solution.md",
    "content": "Dua cek pertama berubah ke `case`. Cek ketiga dipecah menjadi dua case:\n\n```js run\nlet a = +prompt('a?', '');\n\nswitch (a) {\n  case 0:\n    alert( 0 );\n    break;\n\n  case 1:\n    alert( 1 );\n    break;\n\n  case 2:\n  case 3:\n    alert( '2,3' );\n*!*\n    break;\n*/!*\n}\n```\n\nTolong ingat: `break` di paling bawah tidak wajib. Tapi kita taruh itu supaya kodenya future-proof.\n\nDi masa depan, ada kans bahwa kita ingin menambah `case` lebih, misalnya `case 4`. Dan jika kita lupa menambah break sebelum itu, di akhir `case 3`, akan ada error. Jadi itu lebih ke semacam asuransi diri.\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/2-rewrite-if-switch/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Tulis kembali \"if\" ke dalam \"switch\"\n\nTulis ulang kode berikut menggunakan pernyataan `switch` tunggal:\n\n```js run\nlet a = +prompt('a?', '');\n\nif (a == 0) {\n  alert( 0 );\n}\nif (a == 1) {\n  alert( 1 );\n}\n\nif (a == 2 || a == 3) {\n  alert( '2,3' );\n}\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/14-switch/article.md",
    "content": "# Pernyataan \"switch\"\n\nPernyataan `switch` bisa mengganti pengecekan ganda `if`.\n\nIa memberi cara lebih deskriptif untuk membandingkan nilai dengan varian ganda.\n\n## Syntax\n\n`switch` punya satu atau lebih blok `case` dan satu default opsional.\n\nRupanya seperti ini:\n\n```js no-beautify\nswitch(x) {\n  case 'value1':  // if (x === 'value1')\n    ...\n    [break]\n\n  case 'value2':  // if (x === 'value2')\n    ...\n    [break]\n\n  default:\n    ...\n    [break]\n}\n```\n\n- Nilai `x` dicek ekaulitasnya secara ketat dengan nilaid dari `case` pertama (yaitu, `value1`) lalu ke kedua (`value2`) dan seterusnya.\n- Jika ekualitas ditemukan, `switch` mulai mengexekusi kode mulai dari `case` yang berkorespondensi, hingga `break` terdekat (atau hingga akhir `switch`).\n- Jika tak ada case yang cocok maka kode `default` diexekusi (jika ada).\n\n## Contoh\n\nContoh `switch` (kode yang dieksekusi dihighlight):\n\n```js run\nlet a = 2 + 2;\n\nswitch (a) {\n  case 3:\n    alert( 'Too small' );\n    break;\n*!*\n  case 4:\n    alert( 'Exactly!' );\n    break;\n*/!*\n  case 5:\n    alert( 'Too big' );\n    break;\n  default:\n    alert( \"I don't know such values\" );\n}\n```\n\nDi sini `switch` mulai membandingkan `a` dari varian `case` pertama yang bernilai `3`. Kecocokan gagal.\n\nLalu `4`. Ada kecocokan, jadi exekusi mulai dari `case 4` hingga `break` terdekat.\n\n**Jika tak ada `break` maka exekusi lanjut dengan `case` berikutnya tanpa pengecekan.**\n\nContoh tanpa `break`:\n\n```js run\nlet a = 2 + 2;\n\nswitch (a) {\n  case 3:\n    alert( 'Too small' );\n*!*\n  case 4:\n    alert( 'Exactly!' );\n  case 5:\n    alert( 'Too big' );\n  default:\n    alert( \"I don't know such values\" );\n*/!*\n}\n```\n\nDi contoh di atas kita akan lihat exekusi sekuensial dari tiga `alert`:\n\n```js\nalert( 'Exactly!' );\nalert( 'Too big' );\nalert( \"I don't know such values\" );\n```\n\n````smart header=\"Expresi apapun bisa berupa argumen `switch/case`\"\nBaik `switch` maupun `case` membolehkan sembarang expresi.\n\nMisalnya:\n\n```js run\nlet a = \"1\";\nlet b = 0;\n\nswitch (+a) {\n*!*\n  case b + 1:\n    alert(\"this runs, because +a is 1, exactly equals b+1\");\n    break;\n*/!*\n\n  default:\n    alert(\"this doesn't run\");\n}\n```\nDi sini `+a` memberikan `1`, yang dibandingkan dengan `b + 1` ndalam `case`, and the corresponding code is executed.\n````\n\n## Pengelompokan \"case\"\n\nBeberapa varian `case` yang berbagi kode yang sama bisa dikelompokkan.\n\nMisalnya, jika kita ingin kode yang sama berjalan untuk `case 3` dan `case 5`:\n\n```js run no-beautify\nlet a = 3;\n\nswitch (a) {\n  case 4:\n    alert('Right!');\n    break;\n\n*!*\n  case 3: // (*) grouped two cases\n  case 5:\n    alert('Wrong!');\n    alert(\"Why don't you take a math class?\");\n    break;\n*/!*\n\n  default:\n    alert('The result is strange. Really.');\n}\n```\n\nSekarang baik `3` maupun `5` menampilkan pesan yang sama.\n\nKemampuan \"mengelompokkan\" case adalah efek samping dari bagaimana `switch/case` bekerja tanpa `break`. Di sini exekusi dari `case 3` mulai dari baris `(*)` dan tembus ke `case 5`, karena tidak ada `break`.\n\n## Tipe berpengaruh\n\nMari kita tekankan bahwa pengecekan ekualitas selalu ketat. Nilainya harus bertipe sama supaya cocok.\n\nMisalnya, mari kita pertimbangkan kode ini:\n\n```js run\nlet arg = prompt(\"Enter a value?\");\nswitch (arg) {\n  case '0':\n  case '1':\n    alert( 'One or zero' );\n    break;\n\n  case '2':\n    alert( 'Two' );\n    break;\n\n  case 3:\n    alert( 'Never executes!' );\n    break;\n  default:\n    alert( 'An unknown value' );\n}\n```\n\n1. Untuk `0`, `1`, `alert` pertama berjalan.\n2. Untuk `2` `alert` kedua berjalan.\n3. Tapi untuk `3`, hasil dari `prompt` ialah string `\"3\"`, yang berbeda secara ketat `===` dengan angka `3`. Jadi kita punya kode mati di `case 3`! Varian `default` akan diexekusi.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/1-if-else-required/solution.md",
    "content": "Tidak ada perbedaan."
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/1-if-else-required/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Apakah \"else\" dibutuhkan ?\n\nFungsi berikut mengembalikan nilai `true` jika parameter `age` lebih besar daripada `18`.\n\nJika tidak, fungsi tersebut akan meminta konfirmasi dan mengembalikan nilainya:\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n*!*\n  } else {\n    // ...\n    return confirm('Did parents allow you?');\n  }\n*/!*\n}\n```\n\nakankah fungsi bekerja berbeda jika `else` dibuang ?\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n  }\n*!*\n  // ...\n  return confirm('Did parents allow you?');\n*/!*\n}\n```\napakah ada perbedaan pada tingkah laku dari kedua variasi ?\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/solution.md",
    "content": "Menggunakan tanda tanya operator `'?'`:\n\n```js\nfunction checkAge(age) {\n  return (age > 18) ? true : confirm('Did parents allow you?');\n}\n```\n\nUsing OR `||` (the shortest variant):\nMenggunakan OR `||` (variasi yang terpendek):\n\n```js\nfunction checkAge(age) {\n  return (age > 18) || confirm('Did parents allow you?');\n}\n```\n\nCatatan bahwa tanda kurung sekitar `age > 18` tidak dibutuhkan disini. Mereka ada hanya untuk lebih enak dibaca.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/2-rewrite-function-question-or/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Tulis ulang fungsi menggunakan '?' atau '||'\n\nFungsi berikut mengembalikan nilai `true` jika parameter `age` lebih besar daripada `18`.\n\nJika tidak, fungsi akan meminta sebuah konfirmasi dan mengembalikan nilainya.\n\n\n```js\nfunction checkAge(age) {\n  if (age > 18) {\n    return true;\n  } else {\n    return confirm('Did parents allow you?');\n  }\n}\n```\n\nTulis ulang fungsi, untuk melakukan dengan sama, tetapi tanpa `if`, dalam satu baris.\n\nBuatlah dua variasi dari `checkAge`:\n\n1. Menggunakan sebuah tanda tanya operator `?`\n2. Mengguunakan OR `||`\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/3-min/solution.md",
    "content": "Solusi menggunakan `if`:\n\n```js\nfunction min(a, b) {\n  if (a < b) {\n    return a;\n  } else {\n    return b;\n  }\n}\n```\n\nSolusi menggunakan tanda tanya operator `'?'`:\n\n```js\nfunction min(a, b) {\n  return a < b ? a : b;\n}\n```\n\nP.S. Pada kasus persamaan `a == b` tidak menjadi penting apa yang dikembalikan"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/3-min/task.md",
    "content": "nilai penting: 1\n\n---\n\n# Fungsi min(a,b)\n\nTulis sebuah fungsi `min(a,b)` yang mengembalikan nilai paling terkecil dari dua angka `a` dan `b`.\n\nSebagai gambaran:\n\n```js\nmin(2, 5) == 2\nmin(3, -1) == -1\nmin(1, 1) == 1\n```\n\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/4-pow/solution.md",
    "content": "\n```js menjalankan demo\nfunction pow(x, n) {\n  let result = x;\n\n  for (let i = 1; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", '');\nlet n = prompt(\"n?\", '');\n\nif (n < 1) {\n  alert(`Power ${n} is not supported, use a positive integer`);\n} else {\n  alert( pow(x, n) );\n}\n```\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/4-pow/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Fungsi pow(X,n)\n\nTulis sebuah fungsi `pow(x,n)` yang mengembalikkan nilai `x` pada pangkat `n`. Atau, dengan kata lain, kalikan `x` dengan dirinya sendiri sebanyak `n` kali dan mengembalikan hasilnya.\n\n```js\npow(3, 2) = 3 * 3 = 9\npow(3, 3) = 3 * 3 * 3 = 27\npow(1, 100) = 1 * 1 * ...* 1 = 1\n```\n\nBuatlah sebuah halaman website yang meminta untuk nilai `x` dan `n`, dan tampilkan hasilnya pada `pow(x,n)`.\n\n[demo]\n\nP.S. pada tugas ini, fungsi seharusnya mendukung hanya nilai bilangan natural dari `n`: bilangan integer mulai dari `1`.\n"
  },
  {
    "path": "1-js/02-first-steps/15-function-basics/article.md",
    "content": "# Fungsi\n\nSering kali, kita harus melakukan tindakan yang sama pada skrip di banyak tempat\n\nSebagai contoh, kita mengharuskan untuk menampilkan pesan yang terlihat indah ketika pengunjung melakukan log in, log out dan mungkin di tempat lain.\n\nFungsi adalah program utama yang membentuk \"struktur bangunan\". Mereka memungkinkan kode untuk dipanggil sebanyak mungkin tanpa harus mengetik berulang-ulang.\n\nKita telah melihat contoh dari fungsi built-in, seperti `alert(message)`, `prompt(message, default)` dan `confirm(question)`.\n\n## Deklarasi Fungsi\n\nUntuk membuat fungsi, kita dapat menggunakan *deklarasi fungsi*.\n\nItu terlihat seperti ini:\n\n```js\nfunction showMessage() {\n  alert( 'Hello everyone!' );\n}\n```\n\nKatakunci `fungsi` ditulis duluan, lalu *nama fungsinya*, kemudian daftar semua *parameter* antara tanda kurung () (pada contoh di atas, tanda kurung kosong) dan bagian terakhir adalah fungsi kode, yang juga disebut sebagai \"badan fungsi\", antara kurung kurawal {}.\n\n```js\nfunction name(parameter1, parameter2, ... parameterN) {\n  ...body...\n}\n```\n\nFungsi baru kita dapat disebut dengan nama: `showMessage()`.\n\nSebagai contoh:\n\n```js run\nfunction showMessage() {\n  alert( 'Hello everyone!' );\n}\n\n*!*\nshowMessage();\nshowMessage();\n*/!*\n```\n\nPemanggilan fungsi `showMessage()` mengeksekusi fungsi kode. Disini kita akan melihat pesan keluaran sebanyak dua kali.\n\nContoh ini secara jelas memaparkan satu fungsi utama dari penggunaan fungsi: untuk menghindari duplikasi kode.\n\nJika kita ingin mengubah pesan atau bagaimana pesan itu ingin ditampilkan, itu cukup untuk mengubah kode di satu tempat: yaitu fungsi yang menampilkannya.\n\n## Variabel lokal\n\nVariabel yang diumumkan dalam fungsi hanya akan terlihat di dalam fungsi tersebut.\n\nMisalnya:\n\n```js run\nfunction showMessage() {\n*!*\n  let message = \"Hello, I'm JavaScript!\"; // variabel lokal\n*/!*\n\n  alert( message );\n}\n\nshowMessage(); // Halo, saya adalah  JavaScript!\n\nalert( message ); // <-- Error! Variabel terlihat secara lokal menurut fungsi\n```\n\n## Variabel luar\n\nSuatu fungsi juga dapat mengakses variabel luar, sebagai contoh: \n\n```js run no-beautify\nlet *!*userName*/!* = 'John';\n\nfunction showMessage() {\n  let message = 'Hello, ' + *!*userName*/!*;\n  alert(message);\n}\n\nshowMessage(); // Halo, John\n```\n\nFungsi memiliki hak akses penuh kepada variabel luar fungsi. Juga variabel tersebut dapat diubah.\n\nSebagai contoh:\n\n```js run\nlet *!*userName*/!* = 'John';\n\nfunction showMessage() {\n  *!*userName*/!* = \"Bob\"; // (1) changed the outer variable\n\n  let message = 'Hello, ' + *!*userName*/!*;\n  alert(message);\n}\n\nalert( userName ); // *!*John*/!* sebelum pemanggilan fungsi\n\nshowMessage();\n\nalert( userName ); // *!*Bob*/!*, nilai dimodifikasi oleh fungsi\n```\n\nVariabel luar hanya dapat digunakan jika tidak ada variabel lokal yang menggunakan.\n\nJika terdapat variabel yang memiliki nama identik yang dideklarasikan di dalam fungsi, lalu variabel luar akan *tertumpukkan*. Sebagai gambaran, pada kode di bawah, fungsi menggunakan variabel lokal bernama `userName`. Variabel luar akan terabaikan:\n\n```js run\nlet userName = 'John';\n\nfunction showMessage() {\n*!*\n  let userName = \"Bob\"; // deklarasikan lokal variabel\n*/!*\n\n  let message = 'Hello, ' + userName; // *!*Bob*/!*\n  alert(message);\n}\n\n// fungsi akan membuat dan menggunakan userName dirinya sendiri\nshowMessage();\n\nalert( userName ); // *!*John*/!*, tidak berubah, fungsi tidak dapat mengakses variabel luar\n```\n\n```smart header=\"Global variables\"\nVariabel yang dideklarasikan di luar dari fungsi, seperti variabel luar `userName` pada kode di atas, disebut sebagai *global*.\n\nVariabel global terlihat dari semua fungsi (terkecuali jika ditumpukkan oleh variabel lokal).\n\nIni menjadi suatu cara yang baik untuk mengurangi penggunaan variabel global. Kode yang modern hanya memiliki sedikit bahkan tidak ada variabel global. Kebanyakan variabel dideklarasikan dan digunakan di dalam fungsi masing-masing. Kadang-kadang, mereka dapat digunakan untuk menyimpan data setingkat projek.\n```\n\n## Parameters\n\nKita dapat meloloskan data yang begitu acak kepada fungsi sebagai parameter (disebut juga sebagai *fungsi argumen*).\n\nPada contoh di bawah, fungsi memiliki dua paramter: `from` dan `text`.\n\n```js run\nfunction showMessage(*!*from, text*/!*) { // parameters: from, text\n  alert(from + ': ' + text);\n}\n\n*!*\nshowMessage('Ann', 'Hello!'); // Ann: Hallo! (*)\nshowMessage('Ann', \"What's up?\"); // Ann: Ada apa? (**)\n*/!*\n```\n\nKetika fungsi dipanggil pada penanda `(*)` dan `(**)`, nilai yang diberikan dipindahkan ke variabel lokal `from` dan `text`. Lalu fungsi menggunakan nilai-nilai tersebut.\n\nIni terdapat satu lagi contoh: kita memiliki variabel `from` dan memindahkannya ke fungsi. Dengan catatan: fungsi akan mengubah `from`, tapi perubahan ini tidak akan terlihat di luar fungsi, karena sebuah fungsi akan selalu mendapatkan salinan nilai:\n\n```js run\nfunction showMessage(from, text) {\n\n*!*\n  from = '*' + from + '*'; // membuat \"from\" terlihat lebih indah\n*/!*\n\n  alert( from + ': ' + text );\n}\n\nlet from = \"Ann\";\n\nshowMessage(from, \"Hello\"); // *Ann*: Hallo\n\n// Nilai dari \"from\" adalah sama, fungsi melakukan perubahan pada variabel lokal\nalert( from ); // Ann\n```\n\nKetika sebuah nilai dilewatkan sebagai parameter fungsi, itu juga disebut *argumen*.\n\nDengan kata lain, untuk meluruskan istilah-istilah ini:\n\n- Parameter adalah variabel yang tercantum di dalam tanda kurung dalam deklarasi fungsi (ini adalah istilah waktu deklarasi)\n- Argumen adalah nilai yang diteruskan ke fungsi saat dipanggil (ini adalah istilah waktu panggilan).\n\nKami mendeklarasikan fungsi yang mencantumkan parameternya, lalu memanggilnya lewat argumen.\n\nDalam contoh di atas, seseorang mungkin mengatakan: \"fungsi `sayMessage` dideklarasikan dengan dua parameter, kemudian dipanggil dengan dua argumen: `from` dan `\"Hello\"`\".\n\n\n## Nilai default\n\nJika suatu fungsi dipanggil, tetapi argumen tidak diberikan, maka nilai yang sesuai menjadi `tidak terdefinisi`.\nSebagai gambaran, fungsi yang telah tersebut di atas `showMessage(from, text)` dapat dipanggil dengan argumen tunggal:\n\n```js\nshowMessage(\"Ann\");\n```\n\n\nItu tidak terjadi kesalahan. Malah pemanggilan tersebut akan menghasilkan `\"Ann: undefined\"`. Tidak ada argumen untuk parameter `text`, jadi ini diasumsikan bahwa `text === undefined`.\n\nJika kita ingin menggunakan suatu `text` \"default\" pada kasus ini, lalu kita dapat menentukannya setelah `=`:\n\n```js run\nfunction showMessage(from, *!*text = \"no text given\"*/!*) {\n  alert( from + \": \" + text );\n}\n\nshowMessage(\"Ann\"); // Ann: tidak diberikan teks\n```\n\nSekarang, jika parameter `text` tidak ditentukan, parameter tersebut akan mengambil nilai `\"no text give\"`\n\nDisini `\"no text give\"` adalah string, tapi ia bisa menjadi suatu expresi nilai lebih kompleks, yang hanya dievaluasi dan ditetapkan jika tak ada nilai pada parameter. Jadi, ini juga mungkin ditetapkan:\n\n```js run\nfunction showMessage(from, text = anotherFunction()) {\n  // anotherFunction() hanya akan mengeksekusi jika tidak adanya teks\n  // hasilnya menjjadi nilai pada teks\n}\n```\n\n```smart header=\"Evaluasi parameter default\"\nDi Javascript, parameter default dievaluasi tiap kali fungsi dipanggil tanpa parameter.\n\nPada contoh di atas, `anotherFunction()` dipanggil tiap kali `showMessage()` dipanggil tanpa parameter `text`.\n\nDi sisi lain, itu dipanggil secara independen setiap kali `teks` hilang.\n\n```\n\n### Alternatif nilai default parameter\n\nTerkadang akan dapat dimengerti untuk memberikan nilai default untuk variabel bukan didalam deklarasi fungsi, tapi di tahap selanjutnya, didalam proses eksekusinya.\n\nUntuk memeriksa parameter yang tidak ada, kita bisa membandingkannya dengan `undefined`:\n\n```js run\nfunction showMessage(text) {\n  // ...\n\n*!*\n  if (text === undefined) { // if the parameter is missing\n    text = 'empty message';\n  }\n*/!*\n\n  alert(text);\n}\n\nshowMessage(); // empty message\n```\n\n...Atau kita bisa menggunakan operator `||`:\n\n```js\n// jika teks parameter tidak ada atau \"\", set variabel ke 'empty'\nfunction showMessage(text) {\n  // if text is undefined or otherwise falsy, set it to 'empty'\n  text = text || 'empty';\n  ...\n}\n```\n\nJavascript yang modern mendukung [nullish coalescing operator/operator penggabung nullish](info:nullish-coalescing-operator) `??`, akan lebih baik jika nilai falsy, seperti `0`, dianggap biasa:\n\n```js run\n// jika tidak ada parameter \"count\", tampilkan \"unknown\"\nfunction showCount(count) {\n  // if count is undefined or null, show \"unknown\"\n  alert(count ?? \"unknown\");\n}\n\nshowCount(0); // 0\nshowCount(null); // unknown\nshowCount(); // unknown\n```\n\n## Mengembalikan nilai\n\nFungsi dapat mengembalikan nilai kepada kode pemanggil sebagai hasil akhir.\n\nContoh yang paling sederhana adalah fungsi yang menjumlahkan dua nilai:\n\n```js run no-beautify\nfunction sum(a, b) {\n  *!*return*/!* a + b;\n}\n\nlet result = sum(1, 2);\nalert( result ); // 3\n```\n\nPenulisan kata `return` dapat ditulis dimana saja pada fungsi. Ketika proses eksekusi kode mencapai kata tersebut, proses eksekusi akan berhenti, dan nilai akan dikembalikan kepada kode pemanggil (yang ditentukan pada variabel `result` di atas).\n\nDapat dimungkinkan kehadiran banyak kata `return` pada suatu fungsi tunggal. Misalnya:\n\n```js run\nfunction checkAge(age) {\n  if (age >= 18) {\n*!*\n    return true;\n*/!*\n  } else {\n*!*\n    return confirm('Do you have permission from your parents?');\n*/!*\n  }\n}\n\nlet age = prompt('How old are you?', 18);\n\nif ( checkAge(age) ) {\n  alert( 'Access granted' );\n} else {\n  alert( 'Access denied' );\n}\n```\n\nSangat dimungkinkan menggunakan kata `return` tanpa nilai. Hal ini akan menyebabkan fungsi untuk langsung keluar.\n\nMisalnya:\n\n```js\nfunction showMovie(age) {\n  if ( !checkAge(age) ) {\n*!*\n    return;\n*/!*\n  }\n\n  alert( \"Showing you the movie\" ); // (*)\n  // ...\n}\n```\n\nPada contoh kode di atas, jika `checkAge(age)` mengembalikan nilai `false`, maka `showMovie` tidak akan memproses `alert`.\n\n````smart header=\"Jika fungsi tidak mengembalikan nilai, hal ini sama saja dengan mengembalikan nilai `undefined`\"\n\n```js run\nfunction doNothing() { /* kosong */ }\n\nalert( doNothing() === undefined ); // true\n```\n\n`return` kosong tanpa nilai memiliki nilai yang sama dengan `return undefined`:\n\n```js run\nfunction doNothing() {\n  return;\n}\n\nalert( doNothing() === undefined ); // true\n```\n````\n\n````warn header=\"Jangan tambahkan baris baru diantara `return` dan nilainya\"\n\nUntuk ekspresi yang lebih panjang pada penggunaan `return`, ini mungkin akan menciptakan suatu penulisan yang singkat untuk menuliskannya pada baris yang berbeda, seperti contoh berikut:\n\n```js\nreturn\n (some + long + expression + or + whatever * f(a) + f(b))\n```\nHal ini tidak akan berhasil karena Javascript akan menganggap tanda titik koma setelah kata `return`. Hal ini juga akan berlangsung sama dengan contoh berikut:\n\n```js\nreturn*!*;*/!*\n (some + long + expression + or + whatever * f(a) + f(b))\n```\n\nJadi, ia efektif menjadi kembalian kosong.\n\nJika kita ingin expresi kembalian membungkus beberapa baris, kita mesti mulai di baris yang sama dengan `return`. Atau minimal taruh tanda kurung pembuka di sana seperti ini:\n\n```js\nreturn (\n  some + long + expression\n  + or +\n  whatever * f(a) + f(b)\n  )\n```\nDan ia akan berjalan seperti harapan kita.\n````\n\n## Menamakan fungsi [#penamaan fungsi]\n\nFungsi adalah tindakan. Sehingga nama fungsi mencerminkan kata kerja. Ia harus ringkas, sebisa mungkin harus akurat dan menjelaskan fungsi apa yang dikerjakan, sehingga ketika seseorang yang membaca kode tersebut mendapatkan penjelasan atau indikasi fungsi apa tersebut.\n\nSudah menjadi khalayak umum bahwa untuk membuat fungsi harus dibarengi dengan awalan verbal yang secara tidak langsung menjelaskan tindakannya.\n\nSebagai gambaran, fungsi yang dimulai dengan kata `\"show\"` biasanya melakukan tindakan menunjukkan sesuatu.\n\nFungsi yang dimulai dengan...\n\n- `\"get\"` -- mengembalikan suatu nilai,\n- `\"calc\"` -- menghitungkan sesuatu,\n- `\"create\"` -- membuat sesuatu,\n- `\"check\"` -- melakukan pengecekkan dan mengembalikan nilai boolean, dst.\n\nContoh dari nama yang diberikan di atas:\n\n```js no-beautify\nshowMessage(..)     // menampilkan pesan\ngetAge(..)          // mengembalikan nilai umur (bagaimanapun mengembalikkan umur)\ncalcSum(..)         // menghitung penjumlahan dan mengembalikan hasilnya\ncreateForm(..)      // membuat formulir (dan biasanya mengembalikan nilai)\ncheckPermission(..) // pengecekkan terhadap ijin, mengembalikan true/false\n```\n\nDengan awalan yang tertera, secara sekilas pada nama fungsi memberikan pemahaman tindakan apa yang dilakukan dan nilai apa yang dikembalikan.\n\n```smart header=\"Satu fungsi -- Satu aksi\"\nFungsi sebaiknya mengerjakan apa yang telah ditulis pada namanya, tidak lebih.\n\nDua tindakan independen biasanya berasal dari dua fungsi, walaupun mereka dipanggil secara bersamaan (pada kasus ini, kita mampu membuat fuungsi ketiga yang memanggil keduanya).\n\nSedikit contoh yang mematahkan aturan ini:\n\n- `getAge` -- akan menjadi buruk jika menunjukkan `alert` yang menunjukkan umur (seharusnya hanya get).\n- `createForm` -- akan menjadi buruk jika fungsi tersebut mengubah dokumen, menambahkan formulir pada dokumen tersebut (seharusnya hanya membentuk dokumen dan mengembalikannya).\n- `checkPermission` -- akan menjadi buruk jika fungsi tersebut menampilkan pesan `akses diberikan/ditolak` (seharusnya hanya melakukan pengecekkan dan mengembalikkan nilainya).\n\nPada contoh-contoh ini diasumsikan arti-arti umum pada kata awalan. Kamu dan tim kamu memiliki kehendak bebas untuk menentukan arti lainnya, tapi biasanya penentuan arti tersebut tidaklah jauh berbeda. Pada contoh lain, kamu seharusnya memiliki pemahaman yang kuat dari arti kata awalan yang digunakan, kata awalan apa yang dapat digunakan pada fungsi dan tidak dapat diguunakan. Semua kata awalan fungsi harus mengikuti aturan tertentu. Dan tim seharusnya dapat saling memberikan pemahaman satu sama lain.\n```\n\n```smart header=\"Ultrashort function names\"\nFungsi yang *digunakan secara sering* kadang-kadang memiliki nama yang sangat pendek.\n\nSebagai contoh, framework [jQuary](http://jquary.com) mendefinisikan fungsi dengan simbol `$`. Library [Lodash](https://lodash.com) memiliki fungsi inti yang dinamakan dengan `_`.\n\nHal-hal tersebut adalah pengecualian. Secara umum, nama fungsi sebaiknya ringkas dan menjelaskan maksudnya.\n```\n\n## Fungsi == komen\n\nFungsi seharusnya memiliki nama yang pendek dan hanya melakukan satu tindakan. Jika tindakan tersebut mengerjakan hal yang cukup kompleks, mungkin sebaiknya fungsi tersebut dibagi menjadi fungsi yang lebih sederhana. Kadang-kadang, mengikuti aturan ini tidaklah mudah, tetapi tentu adalah hal yang baik.\n\nFungsi yang terpisah bukan hanya mudah untuk diuji coba dan debug -- kehadirannya sangat baik untuk diberikan komentar!\n\nSebagai gambaran, bandingkan dua fungsi `showPrimes(n)` di bawah. Setiap satu dari keluarannya [bilangan prima](https://en.wikipedia.org/wiki/Prime_number) mencapai hingga `n`.\n\nVariasi pertama menggunakan label:\n\n```js\nfunction showPrimes(n) {\n  nextPrime: for (let i = 2; i < n; i++) {\n\n    for (let j = 2; j < i; j++) {\n      if (i % j == 0) continue nextPrime;\n    }\n\n    alert( i ); // bilangan prima\n  }\n}\n```\n\nPada variasi yang kedua mengguunakan fungsi tambahan `isPrime(n)` untuk dilakukan uji coba keutamaannya:\n\n```js\nfunction showPrimes(n) {\n\n  for (let i = 2; i < n; i++) {\n    *!*if (!isPrime(i)) continue;*/!*\n\n    alert(i);  // bilangan prima\n  }\n}\n\nfunction isPrime(n) {\n  for (let i = 2; i < n; i++) {\n    if ( n % i == 0) return false;\n  }\n  return true;\n}\n```\n\nPada variasi yang kedua lebih mudah untuk dipahami, benarkan ? Malah daripada potongan kode yang kita lihat pada tindakan (`isPrime`). Kadang-kadang, orang-orang merujuk kepada penulisan kode yang *menjelaskan dirinya*.\n\nJadi, fungsi dapat dibuat walaupun kita tidak terlalu sering menggunakannya. Mereka membuat kode lebih terstruktur dan lebih mudah untuk dibaca.\n\n## Kesimpulan\n\nDeklarasi fungsi terlihat seperti ini:\n\n```js\nfunction name(parameters, delimited, by, comma) {\n  /* code */\n}\n```\n\n- Nilai yang diberikan kepada fungi sebagai parameter dipindahkan ke variabel lokal.\n- Fungsi mungkin dapat diakses dengan variabel luar. Tetapi fungsi tersebut hanya dapat bekerja melalui internal fungsi keluar. Kode di luar daripada fungsi bersangkutan tidak dapat melihat variabel lokal.\n- Fungsi dapat mengembalikan suatu nilai. Jika tidak demikian, maka akan mengembalikan nilai `undefined`.\n\nUntuk membuat kode yang bersih dan mudah untuk dipahami, sangat dianjurkan untuk menggunakan variabel lokal dan parameter pada fungsi, tidak mengguunakan variabel luar / variabel global.\n\nAkan menjadi hal yang mudah untuk dipahami pada fungsi yang mendapatkan parameter, yang bekerja dengan parameter tersebut  dan mengembalikan nilainya daripada fuungsi yang tidak memilki parameter, tetapi melakukan modifikasi terhadap variabel luar akan memiliki efek samping.\n\nPenamaan fungsi:\n\n- Nama seharusnya ditulis dengan jelas dan mendeskripsikan apa yang dikerjakan. Ketika kita melihat fungsi dipanggil pada kode, penamaan yang baik secara langsuung akan memberikan kita pemahaman apa yang dikerjakan dan nilai apa yang dikembalikan.\n- Fungsi adalah tindakan, sehingga nama fungsi biasanya kata kerja (verbal).\n- Banyak nama-nama awalan fungsi seperti `create`, `show`, `get`, `check` dan lainnya. Gunakan awalan tersebut untuk memberikan kata kunci apa yang dikerjakan oleh fungsi.\n\nFungsi adalah bagaikan fondasi bangunan dari skrip. Sekarang, kita telah mempelajari dasarnya, sehingga sekarang kita dapat memumlai untuk membuat dan menggunakannya. Tapi hal itu baru permulaan dari awal perjalanan. Kita akan kembali menggunakan mereka berulang kali, secara terus-menerus menggunakan secara mendalam hingga fitur yang lebih kompleks.\n"
  },
  {
    "path": "1-js/02-first-steps/16-function-expressions/article.md",
    "content": "# Expresi fungsi\n\nDi JavaScript, fungsi bukan \"struktur bahasa magis\", melaikan satu bentuk nilai spesial.\n\nSyntax yang kita gunakan sebelumnya disebut *Deklarasi Fungsi*:\n\n```js\nfunction sayHi() {\n  alert( \"Hello\" );\n}\n```\n\nAda syntax lain untuk membuat fungsi yang disebut *Expresi Fungsi*.\n\nRupanya seperti ini:\n\n```js\nlet sayHi = function() {\n  alert( \"Hello\" );\n};\n```\n\nDi sini, fungsi dibuat dan diisi variabel secara explisit, seperti nilai lain manapun. Tak peduli bagaimana fungsi didefinisi, ia hanya suatu nilai yang disimpan dalam variabel `sayHi`.\n\nArti dari sampel kode ini sama: \"buatlah fungs dan taruhlah di dalam variabel `sayHi`\".\n\nKita bahkan bisa mencetak nilai itu menggunakan `alert`:\n\n```js run\nfunction sayHi() {\n  alert( \"Hello\" );\n}\n\n*!*\nalert( sayHi ); // menampilkan kode fungsi\n*/!*\n```\n\nTolong ingat bahwa baris terakhir tidak menjalankan fungsi, karena tak ada tanda kurung setelah `sayHi`. Ada bahasa pemrograman di mana satu penyebutan nama fungsi menyebabkan exekusi fungsi, tapi JavaScript tak seperti itu.\n\nDi JavaScript, fungsi adalah nilai, jadi kita bisa menghadapinya sebagai nilai. Kode di atas menunjukkan representasi stringnya, yang mana kode sumbernya.\n\nPastinya, fungsi adalah nilai spesial, dengan anggapan bahwa kita bisa memanggilnya seperti `sayHi()`.\n\nTapi ia tetaplah nilai. Jadi kita bisa memakainya seperti macam nilai lainnya.\n\nKita bisa mengkopi fungsi ke variabel lain:\n\n```js run no-beautify\nfunction sayHi() {   // (1) buat\n  alert( \"Hello\" );\n}\n\nlet func = sayHi;    // (2) kopi\n\nfunc(); // Hello     // (3) jalankan kopinya (ia bekerja)!\nsayHi(); // Hello    //     ini juga masih bekerja (kenapa tidak)\n```\n\nInilah yang terjadi di atas secara detil:\n\n1. Deklarasi Fungsi `(1)` membuat fungsi dan menaruhnya ke variabel bernama `sayHi`.\n2. Baris `(2)` mengkopinya ke variabel `func`. Tolong ingat lagi: tak ada tanda kurung setelah `sayHi`. Jika ada, maka `func = sayHi()` akan menulis  *hasil panggilan* `sayHi()` ke `func`, bukan *fungsi* `sayHi` itu sendiri.\n3. Sekarang fungsi bisa dipanggil baik sebagai `sayHi()` maupun `func()`.\n\nCatat bahwa kita jusa bisa menggunakan Expresi Fungsi untuk mendeklarasi `sayHi`, di baris pertama:\n\n```js\nlet sayHi = function() {\n  alert( \"Hello\" );\n};\n\nlet func = sayHi;\n// ...\n```\n\nSemua akan berjalan sama.\n\n\n````smart header=\"Kenapa ada semicolon di akhir?\"\nKamu mungkin penasaran, kenapa Expresi Fungsi punya semicolon `;` di akhir, tapi Deklarasi Fungsi tidak:\n\n```js\nfunction sayHi() {\n  // ...\n}\n\nlet sayHi = function() {\n  // ...\n}*!*;*/!*\n```\n\nJawabannya simpel:\n- `;` tidak dibutuhkan di akhir blok kode dan struktur syntax yang memakai mereka seperti `if { ... }`, `for {  }`, `function f { }` dll.\n- Expresi Fungsi digunakan di dalam pernyataan: `let sayHi = ...;`, sebagai nilai. Ia bukan blok kode, tapi lebih ke penetapan. Semicolon `;` disarankan di akhir pernyataan, tak peduli nilainya apa. Jadi semicolon di sini tak ada hubungannya dengan Expresi Fungsi itu sendiri, ia hanya menstop pernyataan.\n````\n\n## Fungsi callback\n\nAyo kita lihat pada contoh lain mengoper fungsi sebagai nilai dan menggunakan expresi fungsi.\n\nKita akan menulis fungsi `ask(question, yes, no)` dengan tiga parameter:\n\n`question`\n: Teks pertanyaan\n\n`yes`\n: Fungsi yang berjalan jika jawabannya \"Yes\"\n\n`no`\n: Fungsi yang berjalan jika jawabannya \"No\"\n\nFungsinya akan menanyakan `question` dan, tergantung jawabannya pengguna, panggil `yes()` atau `no()`:\n\n```js run\n*!*\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes()\n  else no();\n}\n*/!*\n\nfunction showOk() {\n  alert( \"You agreed.\" );\n}\n\nfunction showCancel() {\n  alert( \"You canceled the execution.\" );\n}\n\n// usage: functions showOk, showCancel are passed as arguments to ask\nask(\"Do you agree?\", showOk, showCancel);\n```\n\nPada praktiknya, fungsi macam ini agak berguna. Perbedaan besar antara `ask` kehidupan-nyata dan contoh di atas adalah fungsi kehidupan-nyata memakai cara komplex untuk berinteraksi dengan pengguna daripada sekedar `confirm`. Di peramban, fungsi macam ini biasanya menarik window pertanyaan menarik. Tapi itu cerita lain lagi.\n\n**Argumen `showOk` dan `showCancel` dari `ask` dipanggil *fungsi callback* atau hanya *callback*.**\n\nIdenya adalah kita mengoper fungsi dan berharap ia \"dipanggil kembali\" kemudian jika dibutuhkan. Pada kasus kita, `showOk` menjadi callback untuk jawaban \"yes\", dan `showCancel` untuk jawaban \"no\".\n\nKita bisa memakai Expresi Fungsi untuk menulis fungsi yang sama lebih pendek:\n\n```js run no-beautify\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes()\n  else no();\n}\n\n*!*\nask(\n  \"Do you agree?\",\n  function() { alert(\"You agreed.\"); },\n  function() { alert(\"You canceled the execution.\"); }\n);\n*/!*\n```\n\nDi sini, fungsi dideklarasi tepat di dalam panggilan `ask(...)`. Mereka tak punya nama, jadinya disebut *anonymous*. Fungsi macam ini tak bisa diakses di luar `ask` (karena mereka tak diset ke variabel), tapi itulah yang kita mau di sini.\n\nKode macam ini muncul di script kita secara sangat alamiah, ia ada di dalam semangat JavaScript.\n\n```smart header=\"Fungsi adalah nilai yang mewakili \\\"aksi\\\"\"\nNilai reguler seperti string atau angka mewakiliki *data*.\n\nFungsi bisa dianggap sebagai *aksi*.\n\nKita bisa mengopernya antara variabel dan menjalankannya kapanpun kita mau.\n```\n\n\n## Expresi Fungsi vs Deklarasi Fungsi\n\nMari kita rumuskan kunci perbedaan antara Deklarasi dan Expresi Fungsi.\n\nPertama, syntax: bagaimana membedakan antara mereka dalam kode.\n\n- *Deklarasi Fungsi:* fungsi, yang dideklarasi sebagai pernyataan terpisah, dalam aliran kode utama.\n\n    ```js\n    // Deklarasi Fungsi\n    function sum(a, b) {\n      return a + b;\n    }\n    ```\n- *Expresi Fungsi:* fungsi, yang dibuat dalam expresi atau dalam konstruksi syntax lain. Di sini, fungsi dibuat pada sisi kanan dari \"expresi penetapan\" `=`:\n\n    ```js\n    // Expresi Fungsi\n    let sum = function(a, b) {\n      return a + b;\n    };\n    ```\n\nPerbedaan yang lebih halus ialah *ketika* fungsi dibuat oleh JavaScript engine.\n\n**Expresi Fungsi dibuat ketika exekusi mencapainya dan hanya dapat dipakai saat itu saja.**\n\nSekali aliran exekusi melewati sisi kanan penetapan `let sum = function…` -- di sinilah, fungsi dibuat dan bisa digunakan (diset, dipanggil, dll. ) dari sekarang.\n\nDeklarasi Fungsi berbeda.\n\n**Deklarasi Fungsi bisa dipanggil lebih cepat dari ia didefinisi.**\n\nMisalnya, Deklarasi Fungsi global terlihat di seluruh script, tak peduli di mana ia berada.\n\nItu karena algoritma internal. Saat JavaScript siap menjalankan script, pertama ia melihat Deklarasi Fungsi global di dalam dan membuat fungsi. Kita bisa anggap itu sebagai \"tahap inisialisasi\".\n\nDan setelah semua Deklarasi Fungsi diproses, kodenya diexekusi. Jadi ia punya akses ke fungsi-fungsi ini.\n\nMisalnya, ini berjalan:\n\n```js run refresh untrusted\n*!*\nsayHi(\"John\"); // Hello, John\n*/!*\n\nfunction sayHi(name) {\n  alert( `Hello, ${name}` );\n}\n```\n\nDeklarasi Fungsi `sayHi` dibuat saat JavaScript siap memulai scriptnya dan terlihat di manapun di dalam.\n\n...Jika ia Expresi Fungsi, maka ia tak akan berjalan:\n\n```js run refresh untrusted\n*!*\nsayHi(\"John\"); // galat!\n*/!*\n\nlet sayHi = function(name) {  // (*) tak ada magic lagi\n  alert( `Hello, ${name}` );\n};\n```\n\nExpresi Fungsi dibuat saat exekusi mencapainya. Itu hanya akan terjadi pada baris `(*)`. Sudah telat banget.\n\nFitur spesial lain dari Deklarasi Fungsi ialah scope blok mereka.\n\n**Di mode ketat, ketika Deklarasi Fungsi berada di jangkauan blok kode, ia terlihat di manapun di dalam blok. Tapi tidak di luarnya.**\n\nMisalnya, bayangkan kita harus mendeklarasi fungsi `welcome()` tergantung variabel `age` yang kita dapat saat runtime. Lalu kita berencana akan menggunakannya kemudian.\n\nJika kita memakai Deklarasi Fungsi, ia tak akan bekerja sesuai harapan:\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\n// secara kondisional mendeklarasi fungsi\nif (age < 18) {\n\n  function welcome() {\n    alert(\"Hello!\");\n  }\n\n} else {\n\n  function welcome() {\n    alert(\"Greetings!\");\n  }\n\n}\n\n// ...pakai ini kemudian\n*!*\nwelcome(); // Error: welcome is not defined\n*/!*\n```\n\nItu karena Deklarasi Fungsi cuma terlihat di dalam blok kode yang ia tinggali.\n\nIni contoh lainnya:\n\n```js run\nlet age = 16; // ambil 16 sebagai contoh\n\nif (age < 18) {\n*!*\n  welcome();               // \\   (berjalan)\n*/!*\n                           //  |\n  function welcome() {     //  |  \n    alert(\"Hello!\");       //  |  Deklarasi Fungsi tersedia\n  }                        //  |  di manapun dalam blok kode tempat dia dideklarasi\n                           //  |\n*!*\n  welcome();               // /   (berjalan)\n*/!*\n\n} else {\n\n  function welcome() {    \n    alert(\"Greetings!\");\n  }\n}\n\n// Di sini kita di luar kurung kurawal,\n// jadi kita tak bisa melihat Deklarasi Fungsi yang dibuat di dalamnya.\n\n*!*\nwelcome(); // Error: welcome is not defined\n*/!*\n```\n\nApa yang bisa kita lakukan supaya `welcome` terlihat di luar `if`?\n\nPendekatan yang benar ialah menggunakan Expresi Fungsi dan menetapkan `welcome` ke variabel yang dideklarasi di luar `if` dan punya visibilitas yang cukup.\n\nKode ini berjalan sesuai harapan:\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome;\n\nif (age < 18) {\n\n  welcome = function() {\n    alert(\"Hello!\");\n  };\n\n} else {\n\n  welcome = function() {\n    alert(\"Greetings!\");\n  };\n\n}\n\n*!*\nwelcome(); // sekarang ok\n*/!*\n```\n\nAtau kita bisa menyederhanakannya lebih lanjut menggunakan operator tanda tanya `?`:\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome = (age < 18) ?\n  function() { alert(\"Hello!\"); } :\n  function() { alert(\"Greetings!\"); };\n\n*!*\nwelcome(); // sekarang ok\n*/!*\n```\n\n\n```smart header=\"Kapan harus memilih Deklarasi Fungsi versus Expresi Fungsi?\"\nSebagai aturan praktis, saat kita harus mendeklarasi fungsi, hal pertama yang kita pertimbangkan ialah syntax Deklarasi Fungsi. Ia memberi kebebasan lebih dalam bagaimana mengorganisir kode kita, karena kita bisa memanggil fungsi macam ini sebelum mereka dideklarasi.\n\nItu juga untuk keterbacaan yang lebih baik, karena lebih mudah melihat `function f(…) {…}` dalam kode ketimbang `let f = function(…) {…}`. Deklarasi Fungsi lebih \"eye-catching\".\n\n...Tapi jika Deklarasi Fungsi tak cocok untuk beberapa alasan, atau kita butuh deklarasi kondisional (kita sudah lihat contohnya), maka Expresi Fungsi sebaiknya digunakan.\n```\n\n## Kesimpulan\n\n- Fungsi adalah nilai. Mereka bisa diset, dikopi atau dideklarasi di kode manapun.\n- Jika fungsi dideklarasi sebagai pernyataan terpisah di aliran kode utama, ia disebut \"Deklarasi Fungsi\".\n- Jika fungsi dibuat sebagai bagian expresi, ia disebut \"Expresi Fungsi\".\n- Deklarasi Fungsi diproses sebelum blok kode diexekusi. Mereka terlihat di manapun dalam blok.\n- Expresi Fungsi dibuat saat aliran exekusi mencapai mereka.\n\nDi banyak kasus saat kita harus mendeklarasi fungsi, Deklarasi Fungsi disenangi, karena ia terlihat sebelum deklarasi itu sendiri. Itu memberi kita flexibilitas lebih dalam organisasi kode, dan biasa lebih mudah terbaca.\n\nJadi sebaiknya kita gunakan Expresi Fungsi hanya saat Deklarasi Fungsi tak cocok digunakan. Kita sudah melihat beberapa contoh itu di bab ini, dan kita akan melihat lebih banyak lagi nanti.\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/solution.md",
    "content": "\n```js run\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n\nask(\n  \"Do you agree?\",\n*!*\n  () => alert(\"You agreed.\"),\n  () => alert(\"You canceled the execution.\")\n*/!*\n);\n```\n\nTerlihat pendek dan bersih, kan?\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/1-rewrite-arrow/task.md",
    "content": "\n# Rewrite with arrow functions\n\nGanti Expresi Fungsi dengan fungsi panah dalam kode berikut:\n\n```js run\nfunction ask(question, yes, no) {\n  if (confirm(question)) yes();\n  else no();\n}\n\nask(\n  \"Do you agree?\",\n  function() { alert(\"You agreed.\"); },\n  function() { alert(\"You canceled the execution.\"); }\n);\n```\n"
  },
  {
    "path": "1-js/02-first-steps/17-arrow-functions-basics/article.md",
    "content": "# Dasar-dasar fungsi Arrow\n\nTerdapat sintaks lain yang sangat sederhana dan ringkas untuk membuat fungsi-fungsi, bahkan sering kali lebih baik ketimbang fungsi-fungsi *Expression*.\n\nDisebut sebagai \"fungsi *arrow* (panah)\", karena sintaks fungsinya terlihat seperti ini:\n\n```js\nlet func = (arg1, arg2, ..., argN) => expression\n```\n\n...Ini membuat sebuah fungsi `func` yang menerima argumen `arg1..argN`, kemudian mengevaluasi `expression` yang ada di sisi kanan serta kegunaannya dan mengembalikan hasilnya.\n\nDalam kata lain, fungsi tersebut adalah versi yang lebih pendek dari:\n\n```js\nlet func = function(arg1, arg2, ..., argN) {\n  return expression;\n};\n```\n\nMari kita lihat contoh konkritnya:\n\n```js run\nlet sum = (a, b) => a + b;\n\n/* Fungsi arrow ini adalah bentuk yang lebih pendek dari:\n\nlet sum = function(a, b) {\n  return a + b;\n};\n*/\n\nalert( sum(1, 2) ); // 3\n```\n\nSeperti yang bisa dilihat, perhatikan `(a, b) => a + b` berarti sebuah fungsi yang menerima dua argumen yang diberinama `a` dan `b`. Ketika eksekusi, fungsi tersebut mengevaluasi ekpresi `a + b` dan mengembalikan hasilnya.\n\n- Jika kita memiliki satu argumen saja, maka *parentheses* di sekitar parameter bisa diabaikan, membuat sintaksnya jadi semakin pendek.\n\n    Sebagai contoh:\n\n    ```js run\n    *!*\n    let double = n => n * 2;\n    // secara kasar sama dengan: let double = function(n) { return n * 2 }\n    */!*\n\n    alert( double(3) ); // 6\n    ```\n\n- Jika tidak ada argumen, *parentheses* akan kosong(tapi harus ditunjukkan):\n\n    ```js run\n    let sayHi = () => alert(\"Hello!\");\n\n    sayHi();\n    ```\n\nFungsi-fungsi *arrow* dapat digunakan dengan cara yang sama dengan fungsi ekpresi (*expression*).\n\nUntuk membuat sebuah fungsi secara dinamis contohnya:\n\n```js run\nlet age = prompt(\"What is your age?\", 18);\n\nlet welcome = (age < 18) ?\n  () => alert('Hello') :\n  () => alert(\"Greetings!\");\n\nwelcome();\n```\n\nFungsi-fungsi *arrow* bisa terlihat tidak familiar dan sulit dibaca pada awalnya, namun hal tersebut bisa cepat berubah seiring dengan mata (kita) yang terbiasa dengan struktur tersebut.\n\nFungsi *arrow* sangat memudahkan untuk sintaks-sintaks sederhana, saat kita terlalu malas untuk menulis banyak kata.\n\n## Fungsi *arrow* multi-baris\n\nContoh-contoh di atas mengambil argumen dari sisi kiri dari `=>` dan mengevaluasi ekpresi di sisi kanan.\n\nTerkadang kita memerlukan sesuatu yang agak sedikit rumit, seperti ekspresi atau pernyataan (*statement*) multi-baris (berbaris-baris). Hal tidaklah tidak mungkin, tapi kita harus menutup ekspresi atau pernyataan tersebut dengan tanda kurung kurawal. Kemudian menggunakan sebuah `return` normal diantaranya.\n\nSeperti ini:\n\n```js run\nlet sum = (a, b) => {  // tanda kurung kurawal membuka fungsi multi-baris\n  let result = a + b;\n*!*\n  return result; // jika kita menggunakan kurung kurawal, selajutnya kita perlu menuliskan \"return\" \n*/!*\n};\n\nalert( sum(1, 2) ); // 3\n```\n\n```smart header=\"More to come\"\nKini kita memuji fungsi arrow karena keringkasannya. Namun tidak hanya itu!\n\nFungsi arrow memiliki fitur-fitur menarik lainnya.\n\nUntuk mempelajarinya lebih mendalam, pertama-tama kita perlu untuk mengetahui aspek-aspek lain dari JavaScript, jadi kita akan kembali (mempelajari) fungsi arrow di bab selanjutnya <info:arrow-functions>.\n\nUntuk sekarang, kita sudah bisa menggunakan fungsi arrow untuk sintaks-sintaks sebaris dan callback.\n```\n\n## Ringkasan\n\nFungsi-fungsi *arrow* itu memudahkan untuk sintaks-sintaks yang hanya sebaris. Fungsi *arrow* juga hadir dengan dua cara:\n\n1. Tanpa tanda kurung kurawal: `(...args) => expression` -- sisi kanan adalah sebuah ekspresi: fungsi tersebut mengevaluasi ekspresi dan mengembalikan hasilnya.\n2. Dengan tanda kurung kurawal: `(...args) => { body }` -- tanda kurung kurawal membuat kita bisa menuliskan pernyataan-pernyataan berbaris-baris/multi-baris dalam fungsi tersebut, tapi kita perlu untuk menulsikan `return` untuk mengembalikan (hasil) sesuatu.\n"
  },
  {
    "path": "1-js/02-first-steps/18-javascript-specials/article.md",
    "content": "# Spesial JavaScript\n\nBab ini secara singkat merekap fitur JavaScript yang sudah kita pelajari sekarang, membayar perhatian khusus ke momen-momen halus.\n\n## Struktur kode\n\nPernyataan didelimisi dengan semicolon:\n\n```js run no-beautify\nalert('Hello'); alert('World');\n```\n\nBiasanya, line-break juga diperlakukan sebagai delimiter, jadi itu juga akan bekerja:\n\n```js run no-beautify\nalert('Hello')\nalert('World')\n```\n\nItu disebut \"penyisipan semicolon otomatis\". Kadang ia tidak bekerja, misalnya:\n\n```js run\nalert(\"There will be an error after this message\")\n\n[1, 2].forEach(alert)\n```\n\nKebanyakan panduan codestyle setuju bahwa kita sebaiknya menaruh semicolon di tiap akhir pernyataan.\n\nSemicolon tak dibutuhkan setelah blok kode `{...}` dan konstruksi syntax dengan mereka yang seperti loop:\n\n```js\nfunction f() {\n  // semicolon tak dibutuhkan setelah deklarasi fungsi\n}\n\nfor(;;) {\n  // semicolon tak dibutuhkan setelah loop\n}\n```\n\n...Tapi meskipun kita taruh semicolon \"extra\" di suatu tempat, itu bukan galat. Ia akan diabaikan.\n\nLebih lanjut di: <info:structure>.\n\n## Mode ketat\n\nUntuk mengaktifkan penuh semua fitur modern JavaScript, kita sebaiknya mulai script dengan `\"use strict\"`.\n\n```js\n'use strict';\n\n...\n```\n\nDirective ini harus ada di paling atas script atau di awal badan fungsi.\n\nTanpa `\"use strict\"`, apapun akan bekerja, tapi beberapa fitur bersikap dengan cara kuno, \"kompatibel\". Secara umum kita akan pilih sikap modern.\n\nBeberapa fitur modern bahasa ini (seperti kelas yang akan kita pelajari di kemudian) mengaktifkan mode ketat secara implisit.\n\nLebih lanjut di: <info:strict-mode>.\n\n## Variabel\n\nBisa dideklarasi menggunakan:\n\n- `let`\n- `const` (konstan, tak bisa berubah)\n- `var` (kuno, akan lihat kemudian)\n\nNama variabel bisa mengandung:\n- Huruf dan digit, tapi karakter pertama bisa tak boleh digit.\n- Karakter `$` dan `_` itu normal, setara dengan huruf.\n- Alfabet non-latin dan hieroglyph juga boleh, tapi jarang dipakai.\n\nVariabel adalah tipe dinamis. Mereka bisa menyimpan nilai apapun:\n\n```js\nlet x = 5;\nx = \"John\";\n```\n\nTerdapat 8 tipe data:\n\n- `number` untuk floating-point(bilangan pecahan) dan integer,\n- `bigint` untuk integer yang sangat panjang,\n- `string` untuk string,\n- `boolean` untuk nilai logik: `true/false`,\n- `null` tipe data dengan nilai tunggal `null`, yang sama dengan \"empty/kosong\" atau \"does not exist/tidak ada nilai\",\n- `undefined` -- tipe data dengan nilai tunggal `undefined`, yang sama dengan \"not assigned/belum didefinisikan\",\n- `object` dan `symbol` -- untuk struktur data yang kompleks dan identifier unik, sampai saat ini kita belum belajar ini.\n\nOperator `typeof` mengembalikan tipe untuk satu nilai, dengan dua pengecualian:\n```js\ntypeof null == \"object\" // galat di bahasa\ntypeof function(){} == \"function\" // fungsi diperlakukan spesial\n```\n\nLebih lanjut di: <info:variables> and <info:types>.\n\n## Interaksi\n\nKita menggunakan peramban sebagai lingkungan kerja, jadi fungsi UI dasar akan menjadi:\n\n[`prompt(question, [default])`](mdn:api/Window/prompt)\n: Menanyakan `question`, dan mengembalikan apa yang pengunjung isikan atau `null` jika mereka mengklik \"cancel\".\n\n[`confirm(question)`](mdn:api/Window/confirm)\n: Menanyakan `question` dan menyarakan memilih antara Ok dan Cancel. Pilihannya dikembalikan sebagai `true/false`.\n\n[`alert(message)`](mdn:api/Window/alert)\n: Menampilkan a `message`.\n\nSemua fungsi ini adalah *modal*, mereka menyela exekusi kode dan mencegah pengunjung dari berinteraksi dengan laman hingga mereka menjawab.\n\nMisalnya:\n\n```js run\nlet userName = prompt(\"Your name?\", \"Alice\");\nlet isTeaWanted = confirm(\"Do you want some tea?\");\n\nalert( \"Visitor: \" + userName ); // Alice\nalert( \"Tea wanted: \" + isTeaWanted ); // true\n```\n\nLebih lanjut di: <info:alert-prompt-confirm>.\n\n## Operator\n\nJavaScript mendukung operator berikut:\n\nArithmatika\n: Regular: `* + - /`, juga `%` untuk remainder dan `**` untuk pangkat bilangan.\n\n    Operator biner plus `+` menggabungkan string. Dan juka ada operan yang string, maka yang lainnya akan diubah menjadi string juga:\n\n    ```js run\n    alert( '1' + 2 ); // '12', string\n    alert( 1 + '2' ); // '12', string\n    ```\n\nPenetapan\n: Ada penetapan simpel: `a = b` dan penetapan kombinasi seperti `a *= 2`.\n\nBitwise\n: Operator bitwise bekerja dengan integer 32-bit di bit-level paling kecil: lihat [docs](mdn:/JavaScript/Reference/Operators/Bitwise_Operators) ketika mereka dibutuhkan.\n\nKondisional\n: Satu-satunya operator dengan tiga parameter: `cond ? resultA : resultB`. Jika `cond` truthy, mengembalikan `resultA`, jika tidak `resultB`.\n\nOperator logika\n: Logika AND `&&` dan OR `||` menyajikan evaluasi sirkuit-pendek dan mengembalikan nilai di mana ia berhenti. Logika NOT `!` mengkonversi operand ke tipe boolean dan mengembalikan nilai kebalikannya.\n\nNullish coalescing operator/Operator penggabung nullish\n: Operator `??` menyediakan cara untuk memilih nilai yang terdefinisikan dari sebuah daftar variabel. Hasil dari `a ?? b` adalah a kecuali jika nilainya `null/undefined`, lalu `b`.\n\nPerbandingan\n: Persamaan nilai `==` dari type yang berbeda akan mengubahnya menjadi angka (kecuali `null` dan `undefined` yang sama dengan nilai itu sendiri), jadi contoh dibawah adalah sama: \n\n    ```js run\n    alert( 0 == false ); // true\n    alert( 0 == '' ); // true\n    ```\n\n    Pembandingan lainnya tentu saja mengubah nilainya menjadi angka.\n\n    Operator pembanding `===` tidak melakukan perubahan tipe: untuk operator pembanding ini, berbeda tipe sama dengan berbeda nilai.\n\n    Nilai `null` dan `undefined` adalah spesial: mereka sama `==` satu sama lainnya dan tidak sama dengan nilai lain manapun.\n\n    Pembanding lebih/kurang dari membandingkan string karakter-demi-karakter, tipe lain akan diubah menjadi angka.\n\nOperator lainnya\n: Ada beberapa operator lainnya, seperti operator koma.\n\nLebih lanjut di: <info:operators>, <info:comparison>, <info:logical-operators>, <info:nullish-coalescing-operator>.\n\n## Loop\n\n- Kita meliput 3 tipe loop:\n\n    ```js\n    // 1\n    while (condition) {\n      ...\n    }\n\n    // 2\n    do {\n      ...\n    } while (condition);\n\n    // 3\n    for(let i = 0; i < 10; i++) {\n      ...\n    }\n    ```\n\n- Variabel yang dideklarasi di loop `for(let...)` terlihat cuma di dalam loop. Tapi kita juga bisa membuang `let` dan memakai kembali variabel yang sudah eksis.\n- Directive `break/continue` membolehkan untuk keluar iterasi loop/current. Guakan label untuk menghancurkan loop bersarang.\n\nDetil di: <info:while-for>.\n\nNanti kita akan pelajari tipe loop lainnya untuk berhadapan dengan object.\n\n## Konstruksi \"switch\"\n\nKonstruksi \"switch\" bisa mengganti pengecekan ganda `if`. Ia memakai `===` (ekualitas ketat) untuk pembandingan.\n\nMisalnya:\n\n```js run\nlet age = prompt('Your age?', 18);\n\nswitch (age) {\n  case 18:\n    alert(\"Won't work\"); // hasil dari prompt adalah string, bukan angka\n    break;\n\n  case \"18\":\n    alert(\"This works!\");\n    break;\n\n  default:\n    alert(\"Any value not equal to one above\");\n}\n```\n\nDetal di: <info:switch>.\n\n## Fungsi\n\nKita meliput tiga cara membuat fungsi di JavaScript:\n\n1. Deklarasi Fungsi: fungsi di aliran kode utama\n\n    ```js\n    function sum(a, b) {\n      let result = a + b;\n\n      return result;\n    }\n    ```\n\n2. Expresi Fungsi: fungsi di dalam kontex expresi\n\n    ```js\n    let sum = function(a, b) {\n      let result = a + b;\n\n      return result;\n    };\n    ```\n\n3. Fungsi panah:\n\n    ```js\n    // expresi di sisi kanan\n    let sum = (a, b) => a + b;\n\n    // atau syntax baris-ganda dengan { ... }, butuh kembalian di sini:\n    let sum = (a, b) => {\n      // ...\n      return a + b;\n    }\n\n    // tanpa argumen\n    let sayHi = () => alert(\"Hello\");\n\n    // dengan argumen tunggal\n    let double = n => n * 2;\n    ```\n\n\n- Fungsi bisa punya variabel lokal: mereka yang dideklarasi dalam badannya. Variabel macam itu cuma terlihat di dalam fungsi.\n- Parameter bisa punya nilai default: `function sum(a = 1, b = 2) {...}`.\n- Fungsi selalu mengembalikan sesuatu. Jika tak ada pernyataan `return`, maka kembaliannya `undefined`.\n\nDetil: lihat <info:function-basics>, <info:arrow-functions-basics>.\n\n## Lebih banyak yang akan datang\n\nIni daftar ringkas fitur JavaScript. Untuk sekarang kita belajar hanya dasar. Kemudian di tutorial nanti kamu akan menemui fitur JavaScript yang lebih canggih dan spesial.\n"
  },
  {
    "path": "1-js/02-first-steps/index.md",
    "content": "# JavaScript Dasar\n\nMari kita belajar dasar-dasar pembuatan skrip."
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/article.md",
    "content": "# Mendebug di Chrome\n\nSebelum menulis kode lebih komplex, ayo kita bahas tentang mendebug.\n\n[Mendebug](https://en.wikipedia.org/wiki/Debugging) ialah proses mencari dan membetulkan galat di dalam script. Semua peramban modern dan kebanyakan lingkungan lain mendukung debugging tools -- UI spesial di developer tools yang membuat debugging jauh lebih mudah. Ia juga membolehkan menjejak kode pelan-pelan untuk melihat apa yang sebenarnya terjadi.\n\nKita akan menggunakan Chrome di sini, karena ia punya cukup fitur, kebanyakan peramban lain punya proses serupa`.\n\n## Panel \"Sources\"\n\nVersi Chrome kamu mungkin terlihat berbeda, tapi tetap kelihatan jelas bedanya.\n\n- Buka [laman contoh](debugging/index.html) di Chrome.\n- Nyalakan developer tools dengan `key:F12` (Mac: `key:Cmd+Opt+I`).\n- Pilih panel `Sources`.\n\nInilah apa yang mesti kamu lihat jika kamu baru melakukannya pertama kali:\n\n![](chrome-open-sources.svg)\n\nTombol toggler <span class=\"devtools\" style=\"background-position:-168px -76px\"></span> membuka tab dengan file.\n\nKlik itu dan pilih `hello.js` di tree view. Inilah yang mestinya muncul:\n\n![](chrome-tabs.svg)\n\nPanel Sources punya 3 bagian:\n\n1. Pane **File Navigator** melist HTML, JavaScript, CSS, dan file lainnya, termasuk image yang dilampirkan ke laman. Extensi Chrome bisa muncul juga di sini.\n2. Pane **Editor Kode** menampilkan kode sumber.\n3. Pane **Javascript Debugging** untuk debugging, kita akan mengexplorasi itu segera.\n\nSekarang kamu bisa mengklik toggler yang sama <span class=\"devtools\" style=\"background-position:-200px -76px\"></span> lagi untuk menyembunyikan daftar sumber daya dan memberi spasi ke kode.\n\n## Konsol\n\nJika kita menekan `key:Esc`, maka konsol terbuka di bawah. Kita bisa mengetik command di sana dan menekan `key:Enter` untuk exekusi.\n\nSetelah pernyataan diexekusi, hasilnya ditampilkan di bawah.\n\nMisalnya, `1+2` menghasilkan `3`, dan `hello(\"debugger\")` tak mengembalikan apa-apa, jadi hasilnya `undefined`:\n\n![](chrome-sources-console.svg)\n\n## Breakpoint\n\nMari periksa apa yang terjadi di dalam kode [laman contoh](debugging/index.html). Di `hello.js`, klik nomor baris `4`. Ya, tepat di digit `4`, bukan di kode.\n\nSelamat! Kamu mengeset breakpoint. Silakan klik juga angka untuk baris `8`.\n\nRupanya mesti seperti ini (biru adalah tempat di mana kamu klik):\n\n![](chrome-sources-breakpoint.svg)\n\n*Breakpoint* ialah poin kode di mana debugger akan otomatis menjeda exekusi JavaScript.\n\nKetika kode dijeda, kita bisa periksa variabel kini, mengexekusi command di konsol dsb. Dengan kata lain, kita bisa mendebug itu.\n\nKita selalu bisa menemukan daftar breakpoint di panel kanan. Ini berguna ketika kita punya banyak breakpoint di berbagai file. Ia membolehkan kita untuk:\n- Lompak cepat ke breakpoint di kode (dengan mengklik itu di panel kanan).\n- Mematikan sementara breakpoint dengan meng-uncheck itu.\n- Buang breakpoint dengan mengklik-kanan dan memilik Remove.\n- ...Dan banyak lagi.\n\n```smart header=\"Breakpoint kondisional\"\n*Klik kanan* di nomor baris bisa membuat breakpoint *kondisional*. Ia hanya terpicu saat expresi yang diberikan truthy.\n\nIni praktis saat kita harus berhenti cuma untuk nilai variabel tertentu atau untuk parameter fungsi tertentu.\n```\n\n## Command debugger\n\nKita juga bisa menjeda kode menggunakan command `debugger` di dalamnya, seperti ini:\n\n```js\nfunction hello(name) {\n  let phrase = `Hello, ${name}!`;\n\n*!*\n  debugger;  // <-- debugger berhenti di sini\n*/!*\n\n  say(phrase);\n}\n```\n\nIni sangat nyaman saat kita di dalam editor kode dan tak ingin berubah ke peramban dan mencari script di developer tools untuk mengeset breakpoint.\n\n\n## Jeda dan lihat sekitar\n\nDi contoh kita, `hello()` dipanggil selama page load, jadi cara termudah untuk mengaktivasi debugger (setelah kita set breakpoint) ialah memuat-ulang laman. Jadi mari tekan `key:F5` (Windows, Linux) atau `key:Cmd+R` (Mac).\n\nKetika breakpoint diset, exekusi terjeda di baris ke-4:\n\n![](chrome-sources-debugger-pause.svg)\n\nSilakan buka dropdown informasional ke kanan (dilabeli panah). Mereka membolehkan kamu memeriksa code state sekarang:\n\n1. **`Watch` -- menampilkan nilai sekarang untuk expresi apapun.**\n\n    Kamu bisa mengklik `+` dan menginput expresi. Debugger akan menampilkan nilainya kapan saja, otomatis merekalkulasi itu di proses exekusi.\n\n2. **`Call Stack` -- menampilkan rantai panggilan bersarang.**\n\n    Di momen sekarang debugger berada di dalam panggilan `hello()`, dipanggil script di `index.html` (tak ada fungsi di sana, jadi ia disebut \"anonymous\").\n\n    If you click on a stack item (e.g. \"anonymous\"), the debugger jumps to the corresponding code, and all its variables can be examined as well.\n3. **`Scope` -- current variables.**\n\n    `Local` menampilkan variabel fungsi lokal. Kita juga bisa melihat nilai mereka yang dihighlight tepat di sebelah kanan kode.\n\n    `Global` has global variables (out of any functions).\n\n    Ada juta katakunci `this` di sana yang tidak kita pelajari sekarang, tapi nanti kemudian.\n\n## Menjejak exekusi\n\nSekarang waktunya *menjejak* script.\n\nAda tombol untuk itu di ujung atas panel kanan. Ayo kita ikuti mereka.\n<!-- https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/Images/src/largeIcons.svg -->\n<span class=\"devtools\" style=\"background-position:-7px -76px\"></span> -- melanjutkan exekusi, hotkey `key:F8`.\n: Melanjutkan exekusi. Jika tak ada breakpoint tambahan, maka exekusi berlanjut dan debugger hilang kontrol.\n\n    Inilah apa yang bisa kita lihat setelah satu klik ke dia:\n\n    ![](chrome-sources-debugger-trace-1.svg)\n\n    Exekusi dilanjutkan, mencapai breakpoint lain di dalam `say()` dan terjeda di sana. Perhatikan \"Call Stack\" di kanan. Ia meningkat satu panggilan lagi. Kita di dalam `say()` sekarang.\n\n<span class=\"devtools\" style=\"background-position:-200px -190px\"></span> -- \"Langkahi\": jalankan command berikutnya, hotkey `key:F9`.\n: Jalankan pernyataan berikutnya. Jika kita klik itu sekarang, `alert` akan muncul.\n\n    Mengklik ini akan melangkahi semua aksi script satu-satu.\n\n<span class=\"devtools\" style=\"background-position:-62px -192px\"></span> -- \"Langkahi atas\": jalankan command berikutnya, tapi *jangan masuk ke fungsi*, hotkey `key:F10`.\n: Serupa dengan command \"Step\" sebelumnya, tapi berbeda jika pernyataan berikutnya berupa panggilan fungsi. Yaitu: bukan built-in, seperti `alert`, tapi fungsi kita sendiri.\n\n    Command \"Langkahi\" masuk ke dalam dan menjeda exekusi di baris pertama, sedangkan \"Kangkangi\" mengexekusi panggilan fungsi bersarang secara tak terlihat, mengabaikan internal fungsi.\n\n    Exekusi kemudian segera dijeda setelah fungsi itu.\n\n    Itu baik jika kita tak tertarik mencaritahu ada apa di dalam panggilan fungsi.\n\n<span class=\"devtools\" style=\"background-position:-4px -194px\"></span> -- \"Langkahi masuk\", hotkey `key:F11`.\n: Serupa dengan \"Langkahi\", tapi berbeda dalam hal panggilan fungsi asynchronous. Jika kamu baru mulai belajar JavaScript, maka kamu bisa abaikan perbedaan, karena kita tak punya panggilan asynchronous sekarang.\n\n    Untuk masa depan, perhatikan bahwa command \"Langkahi\" mengabaikan aksi async, seperti `setTimeout` (panggilan fungsi terjadwal), itu diexekusi nanti. \"Langkahi masuk\" masuk ke dalam kode mereka, menunggu mereka jika perlu. Lihat [DevTools manual](https://developers.google.com/web/updates/2018/01/devtools#async) untuk detil lebih.\n\n<span class=\"devtools\" style=\"background-position:-32px -194px\"></span> -- \"Langkahi keluar\": lanjutkan exekusi hingga akhir fungsi sekarang, hotkey `key:Shift+F11`.\n: Lanjutkan exekusi dan berhenti di baris terakhir dari fungsi sekarang. Ini praktis saat kita tak sengaja masuk ke panggilan bersarang memakai <span class=\"devtools\" style=\"background-position:-200px -190px\"></span>, tapi itu tak menarik bagi kita, dan kita mau lanjut ke ujungnya sesegera mungkin.\n\n<span class=\"devtools\" style=\"background-position:-61px -74px\"></span> -- nyalakan/matikan semua breakpoint.\n: Tombol itu tidak menggerakkan exekusi. Cuma on/off massal untuk breakpoint.\n\n<span class=\"devtools\" style=\"background-position:-90px -146px\"></span> -- nyalakan/matikan jeda otomatis jika ada galat.\n: Ketika menyala, dan developer tools terbuka, galat script otomatis menjeda exekusi. Lalu kita bisa menganalisa variabel untuk melihat apa yang salah. Jadi jika script kita mati karena galat, kita bisa buka debugger, menyalakan opsi ini dan memuat-ulang laman untuk melihat di mana ia mati dan apa kontexnya saat itu.\n\n```smart header=\"Lanjut ke sini\"\nKlilk kanan di satu baris kode membuka context menu dengan opsi hebat yang disebut \"Lanjut ke sini\".\n\nIni praktis saat kita mau pindah beberpaa langkah maju ke baris itu, tapi kita terlalu malas mengeset breakpoint.\n```\n\n## Logging\n\nUntuk mengoutput sesuatu ke konsol dari kode kita, ada fungsi `console.log`.\n\nMisalnya, ini mengoutput nilai dari `0` ke `4` ke konsol:\n\n```js run\n// buka konsol untuk melihat\nfor (let i = 0; i < 5; i++) {\n  console.log(\"value,\", i);\n}\n```\n\nPengguna reguler tak melihat output itu, itu di dalam konsol. Untuk melihat itu, buka panel Console developer tool atau tekan `key:Esc` ketika di panel lain: yang membukakan konsol di bawah.\n\nJika kita punya cukup logging di kode kita, maka kita bisa melihat apa yang terjadi dari record, tanpa debugger.\n\n## Kesimpulan\n\nSeperti yang kita lihat, ada tiga cara utama untuk menjeda script:\n1. Breakpoint.\n2. Pernyataan `debugger`.\n3. Galat (jika dev tools terbuka dan tombol <span class=\"devtools\" style=\"background-position:-90px -146px\"></span> \"menyala\").\n\nKetika terjeda, kita bisa mendebug - periksa variabel dan menjejak kode untuk melihat di mana terjadi kesalahan exekusi.\n\nAda banyak lebih opsi di developer tool dari yang diliput di sini. Manual lengkap ada di <https://developers.google.com/web/tools/chrome-devtools>.\n\nInformasi dari bab ini cukup untuk memulai debugging, tapi nanti, terutama jika kamu melakukan banyak hal-hal berkaitan dengan peramban, silakan masuk ke sana dan mencari kemampuan canggih lainnya dari developer tool.\n\nOh, dan juga kamu bisa mengklik di berbagai tempat dev tools dan cuma melihat apa yang muncul. Itu mungkin rute tercepat untuk mempelajari dev tools. Jangan lupa tentang klik kanan dan context menu!\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/debugging.view/hello.js",
    "content": "function hello(name) {\n  let phrase = `Hello, ${name}!`;\n\n  say(phrase);\n}\n\nfunction say(phrase) {\n  alert(`** ${phrase} **`);\n}\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/debugging.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <script src=\"hello.js\"></script>\n\n  Contoh debugging.\n\n  <script>\n  hello(\"John\");\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/01-debugging-chrome/head.html",
    "content": "<style>\nspan.devtools {\n  display: inline-block;\n  background-image: url(/article/debugging-chrome/largeIcons.svg);\n  height:18px;\n  width:18px;\n}\n</style>\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/1-style-errors/solution.md",
    "content": "\nKamu bisa perhatikan berikut:\n\n```js no-beautify\nfunction pow(x,n)  // <- tak ada spasi diantara arguments\n{  // <- menemukan bracket di baris terpisah\n  let result=1;   // <- tak ada spasi sebelum atau setelah =\n  for(let i=0;i<n;i++) {result*=x;}   // <- tak ada spasi\n  // konten { ... } sebaiknya di baris baru\n  return result;\n}\n\nlet x=prompt(\"x?\",''), n=prompt(\"n?\",'') // <-- secara teknis bisa saja\n// tapi akan lebih baik jika dibuat dua baris, juga jangan gunakan spasi dan jangan lupa tanda ;\nif (n<=0)  // <- tidak ada spasi didalam (n <= 0), dan harus ada baris tambahan diatasnya\n{   // <- pembuka kurung kurawal di baris baru\n  // dibawah - baris yang panjang dapat dibagi menjadi beberapa baris untuk menambah keterbacaan kode\n  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);\n}\nelse // <- bisa menulisnya di baris tunggal seperti \"} else {\"\n{\n  alert(pow(x,n))  // tak ada spasi dan kehilangan ;\n}\n```\n\nVarian yang dibetulkan:\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", \"\");\nlet n = prompt(\"n?\", \"\");\n\nif (n <= 0) {\n  alert(`Power ${n} is not supported,\n    please enter an integer number greater than zero`);\n} else {\n  alert( pow(x, n) );\n}\n```\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/1-style-errors/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Gaya jelek\n\nApa yang salah dengan gaya kode di bawah?\n\n```js no-beautify\nfunction pow(x,n)\n{\n  let result=1;\n  for(let i=0;i<n;i++) {result*=x;}\n  return result;\n}\n\nlet x=prompt(\"x?\",''), n=prompt(\"n?\",'')\nif (n<=0)\n{\n  alert(`Power ${n} is not supported, please enter an integer number greater than zero`);\n}\nelse\n{\n  alert(pow(x,n))\n}\n```\n\nBetulkan.\n"
  },
  {
    "path": "1-js/03-code-quality/02-coding-style/article.md",
    "content": "# Gaya Mengkode\n\nKode kita harus sebisa mungkin bersih dan mudah dibaca.\n\nIni sebenarnya seni pemrograman -- ambil tugas rumit dan mengkodenya dengan cara yang benar dan dapat dibaca manusia. Gaya kode yang baik punya andil besar di situ.\n\n## Syntax\n\nIni adalah satu cheatsheet dengan beberapa aturan yang disarankan (lihat bawah untuk lebih detil):\n\n![](code-style.svg)\n<!--\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n\nlet x = prompt(\"x?\", \"\");\nlet n = prompt(\"n?\", \"\");\n\nif (n < 0) {\n  alert(`Power ${n} is not supported,\n    please enter a non-negative integer number`);\n} else {\n  alert( pow(x, n) );\n}\n```\n\n-->\n\nSekarang kita bahas aturannya dan alasannya secara detil.\n\n```warn header=\"Tak ada aturan yang memaksa\"\nTak ada yang diset sekeras batu di sini. Mereka cuma preferensi gaya saja, buka dogma agama.\n```\n\n### Kurung Kurawal\n\nDi kebanyakan proyek JavaScript kurung kurawal ditulis dalam gaya \"Egyptian\" dengan kurawal buka di baris yang sama dengan katakunci -- bukan di baris baru. Juga harus ada spasi sebelum bracket pembuka, seperti ini:\n\n```js\nif (condition) {\n  // lakukan ini\n  // ...dan itu\n  // ...dan itu\n}\n```\n\nKonstruksi sebaris, seperti `if (condition) doSomething()`, ialah hal penting sampingan. Haruskah kita memakai kurawal sama sekali?\n\nIni adalah varian yang teranotasi jadi kamu bisa menilai sendiri keterbacaan mereka:\n\n1. 😠 Pemula kadang melakukan itu. Buruk! Kurung kurawal tak dibutuhkan:\n    ```js\n    if (n < 0) *!*{*/!*alert(`Power ${n} is not supported`);*!*}*/!*\n    ```\n2. 😠 Pisahkan baris berbeda tanpa kurawal. Jangan pernah lakukan, mudah membuat galat saat menambah baris baru:\n    ```js\n    if (n < 0)\n      alert(`Power ${n} is not supported`);\n    ```\n3. 😏 Sebaris tanpa kurawal - bisa diterima, jika pendek:\n    ```js\n    if (n < 0) alert(`Power ${n} is not supported`);\n    ```\n4. 😃 Varian terbaik:\n    ```js\n    if (n < 0) {\n      alert(`Power ${n} is not supported`);\n    }\n    ```\n\nUntuk kode ringkas, sebaris dibolehkan, misal `if (cond) return null`. Tapi blok kode (varian terakhir) biasanya lebih dapat terbaca.\n\n### Panjang Baris\n\nTak ada orang suka membaca kode horizontal yang panjang. Memisahkan mereka ialah praktik yang baik.\n\nMisalnya:\n```js\n// backtick quote ` memperbolehkan memecah string jadi beberapa baris\nlet str = `\n  ECMA International's TC39 is a group of JavaScript developers,\n  implementers, academics, and more, collaborating with the community\n  to maintain and evolve the definition of JavaScript.\n`;\n```\n\nDan, untuk pernyataan `if`:\n\n```js\nif (\n  id === 123 &&\n  moonPhase === 'Waning Gibbous' &&\n  zodiacSign === 'Libra'\n) {\n  letTheSorceryBegin();\n}\n```\n\nPanjang baris maximum sebaiknya disepakati di level-tim. Biasanya 80 atau 120 karakter.\n\n### Indent\n\nAda dua tipe indent:\n\n- **Indent horizontal: 2 atau 4 spasi.**\n\n    Indentasi horizontal dibuat menggunakan 2 atau 4 spasi atau simbol tab horizontal (kunci `key:Tab`). Pemilihan ini sudah lama jadi perang suci. Spasi jadi lebih umum sekarang ini.\n\n    Satu keutamaan spasi dari tab ialah konfigurasi indent spasi lebih flexibel dari simbol tab.\n\n    Misalnya, kita bisa mengalinea argumen dengan bracket pembuka, seperti ini:\n\n    ```js no-beautify\n    show(parameters,\n         aligned, // 5 padding spasi di kiri\n         one,\n         after,\n         another\n      ) {\n      // ...\n    }\n    ```\n\n- **Indent vertikal: baris kosong untuk memecah kode menjadi blok logika.**\n\n    Bahkan satu fungsi pun sering bisa dibagi jadi blok logika. Di contoh berikut, inisialisasi variabel, loop utama dan hasil kembalian dipecah secara vertikal:\n\n    ```js\n    function pow(x, n) {\n      let result = 1;\n      //              <--\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      //              <--\n      return result;\n    }\n    ```\n\n    Sisipkan extra baris baru di mana ini membantu membuat kode lebih terbaca. Tak boleh lebih dari sembilan baris kode tanpa indentasi vertikal.\n\n### Semicolon\n\nSemicolon sebaiknya ada di tiap ujung pernyataan, bahkan meski jika itu bisa dilewati.\n\nAda bahasa di mana semicolon benar-benar opsional dan jarang dipakai. Tapi di JavaScript, ada kasus di mana line break tidak diinterpretasi sebagai semicolon, membuat kode rentan galat. Lihat lebih lanjut tentang itu di bab <info:structure#semicolon>.\n\nJika kamu programmer JavaScript berpengalaman, kamu bisa pilih gaya kode tanpa semicolon seperti [StandardJS](https://standardjs.com/). Atau, lebih baik memakai semicolon untuk menghindari kemungkinan jurang nista. Mayoritas pengembang menaruh semicolon.\n\n### Level Bersarang\n\nCoba hindari kode bersarang dengan level terlalu dalam.\n\nMisalnya, dalam loop, kadang ide bagus memakai directive [`continue`](info:while-for#continue) untuk menghindari sarang extra.\n\nMisalnya, ketimbang menambah kondisional `if` bersarang seperti ini:\n\n```js\nfor (let i = 0; i < 10; i++) {\n  if (cond) {\n    ... // <- satu lagi level bersarang\n  }\n}\n```\n\nKita bisa tulis:\n\n```js\nfor (let i = 0; i < 10; i++) {\n  if (!cond) *!*continue*/!*;\n  ...  // <- tak ada extra level bersarang\n}\n```\n\nHal serupa bisa dilakukan dengan `if/else` dan `return`.\n\nMisalnya, dua konstruksi berikut identik.\n\nOpsi 1:\n\n```js\nfunction pow(x, n) {\n  if (n < 0) {\n    alert(\"Negative 'n' not supported\");\n  } else {\n    let result = 1;\n\n    for (let i = 0; i < n; i++) {\n      result *= x;\n    }\n\n    return result;\n  }  \n}\n```\n\nOpsi 2:\n\n```js\nfunction pow(x, n) {\n  if (n < 0) {\n    alert(\"Negative 'n' not supported\");\n    return;\n  }\n\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nYang kedua lebih terbaca karena \"kasus spesial\" `n < 0` ditangani segera. Sekali pengecekan selesai kita bisa pindah ke aliran kode \"utama\" tanpa sarang tambahan.\n\n## Penempatan Fungsi\n\nJika kamu menulis beberapa fungsi \"pembantu\" dan kode yang menggunakan mereka, ada tiga cara untuk mengorganisir fungsi.\n\n1. Deklarasi fungsi *di atas* kode yang menggunakan mereka:\n\n    ```js\n    // *!*deklarasi fungsi*/!*\n    function createElement() {\n      ...\n    }\n\n    function setHandler(elem) {\n      ...\n    }\n\n    function walkAround() {\n      ...\n    }\n\n    // *!*kode yang menggunakan mereka*/!*\n    let elem = createElement();\n    setHandler(elem);\n    walkAround();\n    ```\n2. Kode pertama, lalu fungsi\n\n    ```js\n    // *!*kode yang menggunakan mereka*/!*\n    let elem = createElement();\n    setHandler(elem);\n    walkAround();\n\n    // --- *!*fungsi pembantu*/!* ---\n    function createElement() {\n      ...\n    }\n\n    function setHandler(elem) {\n      ...\n    }\n\n    function walkAround() {\n      ...\n    }\n    ```\n3. Campuran: fungsi dideklarasi di mana ia pertama dipakai.\n\nSeringnya, varian kedua jadi pilihan.\n\nItu karena saat membaca kode, kita pertama mau tahu *itu bisa apa*. Jika kode mulai duluan, maka jadi lebih jelas dari awal. Lalu, mungkin kita tak mau membaca fungsinya sama sekali, terutama jika nama mereka deskriptif, sesuai dengan kelakuan mereka.\n\n## Panduan Gaya\n\nPanguan gaya berisi aturan umum tentang \"bagaimana menulis\" kode, misal quote mana yang dipakai, berapa spasi indent, panjang baris maximal, dll. Banyak hal remeh.\n\nSaat semua anggota tim menggunakan panduan gaya yang sama, kodenya terlihat seragam, tak peduli anggota tim mana yang menulisnya.\n\nTentu saja, tim bisa saja  menulis panduan gaya mereka sendiri, tapi biasanya mereka tak butuh. There are many existing guides to choose from.\n\nBeberapa pilihan populer:\n\n- [Panduan Gaya JavaScript Google](https://google.github.io/styleguide/javascriptguide.xml)\n- [Panduan Gaya JavaScript Airbnb](https://github.com/airbnb/javascript)\n- [Idiomatic.JS](https://github.com/rwaldron/idiomatic.js)\n- [StandardJS](https://standardjs.com/)\n- (plus banyak lainnya)\n\nJika kamu pengembang pemula, mulai dengan cheatsheet di awal bab ini. Lalu kamu bisa menjelajah panduan gaya lain untuk mencari ide lebih dan menentukan mana yang terbaik.\n\n## Linter Terotomasi\n\nLinter ialah tool yang bisa otomatis mengecek gaya kodemu dan memberi saran improvisasi.\n\nYang keren dari ini ialah pengecekan gaya sekaligus menemukan bug., like typos in variable or function names. Because of this feature, using a linter is recommended even if you don't want to stick to one particular \"code style\".\n\nBerikut beberapa linting tool terkenal:\n\n- [JSLint](http://www.jslint.com/) -- one of the first linters.\n- [JSHint](http://www.jshint.com/) -- more settings than JSLint.\n- [ESLint](http://eslint.org/) -- probably the newest one.\n\nSemuanya bisa melakukan itu. Penulis ini menggunakan [ESLint](http://eslint.org/).\n\nKebanyakan linter terintegrasi dengan banyak editor populer: cuma mengaktifkan plugin di editor dan mengkonfigurasi gayanya.\n\nMisalnya, untuk ESLint kamu harus melakukan hal ini:\n\n1. Instal [Node.js](https://nodejs.org/).\n2. Instal ESLint dengan command `npm install -g eslint` (npm ialah installer package JavaScript).\n3. Buat file konfig bernama `.eslintrc` di root proyek JavaScriptmu (di folder yang berisi semua filemu).\n4. Instal/aktifkan plugin untuk editormu yang berintegrasi dengan ESLint. Mayoritas editor punya satu.\n\nBerikut contoh file `.eslintrc`:\n\n```js\n{\n  \"extends\": \"eslint:recommended\",\n  \"env\": {\n    \"browser\": true,\n    \"node\": true,\n    \"es6\": true\n  },\n  \"rules\": {\n    \"no-console\": 0,\n    \"indent\": 2\n  }\n}\n```\n\nDi sini directive `\"extends\"` menunjukkan bahwa konfigurasinya berdasarkan set pengaturan \"eslint:recommended\". Setelah itu, kita spesifikasikan punya kita sendiri.\n\nSelain itu, dimungkinkan juga mengunduh set aturan gaya dari web dan mengextend mereka. Lihat <http://eslint.org/docs/user-guide/getting-started> untuk detil lebih tentang instalasi.\n\nJuga IDE tertentu punya linting built-in, yang nyaman tapi tak bisa dikustomisasi seperti ESLint.\n\n## Kesimpulan\n\nSemua aturan syntax yang dideskripsikan di bab ini (dan di referensi panduan gaya) bertujuan untuk meningkatkan keterbacaan kodemu. Mereka dapat diperdebatkan.\n\nSaat kita berpikir tentang menulis kode \"lebih baik\", pertanyaannya ialah haruskah kita tanya diri kita sendiri: \"Apa yang membuat kode dapat lebih terbaca dan lebih mudah dipahami?\" dan \"Apa yang bisa membantu kita menghindari galat?\" Inilah hal utama yang harus dipikirkan saat memilih dan memperdebatkan gaya kode.\n\nMembaca panduan gaya populer akan membuatmu selalu terupdate dengan ide terbaru tentang tren gaya kode dan praktik terbaiknya.\n"
  },
  {
    "path": "1-js/03-code-quality/03-comments/article.md",
    "content": "# Komentar\n\nSeperti yang kita tahu dari bab <info:structure>, komentar bisa sebaris tunggal: mulai dari `//` dan baris-ganda: `/* ... */`.\n\nNormalnya kita pakai mereka untuk menjelaskan bagaimana dan kenapa kode bekerja.\n\nDi awal pandangan, berkomentar itu sudah jelas, tapi pemula sering salah memakainya dalam pemrograman.\n\n## Komentar jelek\n\nPemula cenderung memakai komentar untuk menjelaskan \"ada apa di dalam kode\". Seperti ini:\n\n```js\n// Kode ini akan melakukan ini (...) dan itu (...)\n// ...dan entah apa lagi...\nvery;\ncomplex;\ncode;\n```\n\nTapi di dalam kode yang baik, jumlah komentar \"penjelasan\" seperti ini sebaiknya minimal. Seriusnya, kode harus bisa dimengerti tanpa mereka.\n\nAda aturan besar tentang itu: \"jika kode begitu tak jelas hingga ia butuh komentar, mungkin malah ia harus ditulis ulang\".\n\n### Resep: fungsi faktor keluar\n\nKadang menguntungkan untuk mengganti sepotong kode dengan fungsi, seperti ini:\n\n```js\nfunction showPrimes(n) {\n  nextPrime:\n  for (let i = 2; i < n; i++) {\n\n*!*\n    // cek apakah i angka prima\n    for (let j = 2; j < i; j++) {\n      if (i % j == 0) continue nextPrime;\n    }\n*/!*\n\n    alert(i);\n  }\n}\n```\n\nVarian lebih baiknya, dengan fungsi faktor keluar `isPrime`:\n\n\n```js\nfunction showPrimes(n) {\n\n  for (let i = 2; i < n; i++) {\n    *!*if (!isPrime(i)) continue;*/!*\n\n    alert(i);  \n  }\n}\n\nfunction isPrime(n) {\n  for (let i = 2; i < n; i++) {\n    if (n % i == 0) return false;\n  }\n\n  return true;\n}\n```\n\nSekarang kita mudah mengerti kodenya. Fungsinya itu sendiri menjadi komentar. Kode macam ini disebut *menjelaskan-diri-sendiri*.\n\n### Resep: membuat fungsi\n\nDan jika kita punya \"code sheet\" sangat panjang seperti ini:\n\n```js\n// kita tambah \\whiskey di sini\nfor(let i = 0; i < 10; i++) {\n  let drop = getWhiskey();\n  smell(drop);\n  add(drop, glass);\n}\n\n// kita tambah jus di sini\nfor(let t = 0; t < 3; t++) {\n  let tomato = getTomato();\n  examine(tomato);\n  let juice = press(tomato);\n  add(juice, glass);\n}\n\n// ...\n```\n\nMaka mungkin varian lebih baiknya ialah merefaktornya menjadi fungsi seperti:\n\n```js\naddWhiskey(glass);\naddJuice(glass);\n\nfunction addWhiskey(container) {\n  for(let i = 0; i < 10; i++) {\n    let drop = getWhiskey();\n    //...\n  }\n}\n\nfunction addJuice(container) {\n  for(let t = 0; t < 3; t++) {\n    let tomato = getTomato();\n    //...\n  }\n}\n```\n\nSekali lagi, fungsi mereka sendiri menjelaskan apa yang terjadi. Tak ada yang dikomentari. Dan juga struktur kodenya lebih baik saat dipisah. Kelakuan tiap fungsi jadi lebih jelas, apa yang ia ambil dan yang ia kembalikan.\n\nRelitanya, kita tak bisa menghindar total dari komentar \"penjelasan\". Ada algoritma rumit. Dan ada \"tweak\" pintar dengan tujuan optimisasi. Tapi umumnya kita harus coba menjaga kodenya simpel dan menjelaskan-diri-sendiri.\n\n## Komentar baik\n\nJadi, komentar penjelasan biasanya jelek. Komentar mana yang bagus?\n\nJelaskan arsitekturnya\n: Sediakan overview tingkat-tinggi dari komponen, bagaimana mereka berinteraksi, apa aliran kontrolnya di berbagai situasi... Singkatnya -- gambaran umum dari kode. Ada bahasa spesial [UML](http://wikipedia.org/wiki/Unified_Modeling_Language) untuk membangun diagram arsitektur tingkat-tinggi yang menjelaskan kode. Sangat berfaedah untuk dipelajari.\n\nParameter dan kegunaan fungsi dokumen\n: Ada syntax spesial [JSDoc](http://en.wikipedia.org/wiki/JSDoc) untuk mendokumentasi fungsi: kegunaan, parameter, nilai kembalian.\n\n    Misalnya:\n    ```js\n    /**\n     * Kembalikan x yang diberi pangkat n.\n     *\n     * @param {number} x Angka yang mau dinaikkan.\n     * @param {number} n Pangkat, harus angka asli.\n     * @return {number} x hasil setelah pangkat n.\n     */\n    function pow(x, n) {\n      ...\n    }\n    ```\n\n    Komentar macam ini membolehkan kita memahami maksud fungsi dan memakainya dengan tepat tanpa melihat isi kode.\n\n    Oya, banyak editor seperti [WebStorm](https://www.jetbrains.com/webstorm/) bisa memahami mereka juga dan memakai mereka untuk menyediakan autocomplete dan beberapa pengecekan-kode otomatis.\n\n    Juga, Ada tool seperti [JSDoc 3](https://github.com/jsdoc3/jsdoc) yang bisa menggenerate dokumentasi-HTML dari komentar. Kamu bisa baca informasi lebih tentang JSDoc di <http://usejsdoc.org/>.\n\nKenapa tugas ini diselesaikan begini?\n: Apa yang tertulis itu penting. Tapi apa yang *tak* tertilis mungkin lebih penting lagi untuk memahami yang terjadi. Kenapa tugas ini diselesaikan tepat seperti ini? Kodenya tak memberikan jawaban.\n\n    Jika ada banyak cara menyelesaikan tugas, kenapa harus ini? Apalagi ini belum jelas.\n\n    Tanpa komentar macam ini situasi berikut memungkinkan:\n    1. Kamu (atau kolegamu) membuka kode yang ditulis kapan hari, dan melihat bahwa ini \"suboptimal\".\n    2. Kamu berpikir: \"Koq bego ya saya waktu itu, dan lihat betapa pintar saya sekarang\", dan menulis-ulang menggunakan varian yang \"lebih jelas dan benar\".\n    3. ...Urgensi menulis-ulang itu bagus. Tapi di proses yang kamu lihat adalah solusi \"lebih jelas\" tersebut jadi minus. Kamu bahkan agak lupa kenapa, karena kamu sudah mencobanya di masa lalu. Kamu balikkan ke varian yang benar, tapi waktumu terbuang percuma.\n\n    Komentar yang menjelaskan solusi itu penting. Mereka membantu melanjutkan pengembangan ke arah yang benar.\n\nAda fitur halus dari kodenya? Di mana mereka dipakai?\n: Jika kodenya punya sesuatu yang halus dan kontra-intuitif, itu sudah pasti bagus untuk dikomentari.\n\n## Kesimpulan\n\nTanpa penting dari pengembang yang baik ialah komentar: kehadiran mereka dan kealfaan mereka.\n\nKomentar yang baik membuat kita memelihara kode lebih baik, kembali ke situ setelah delay dan memakainya secara efektif.\n\n**Komentari ini:**\n\n- Arsitektur keseluruhan, pemandangan tingkat-tinggi.\n- Kegunaan fungsi.\n- Solusi penting, terutama yang masih belum jelas.\n\n**Hindari komentar:**\n\n- Yang menjelaskan \"bagaimana kode bekerja\" dan \"apa kelakuannya\".\n- Menaruh mereka hanya jika sudah tak mungkin menyederhanakan kode dan menjelaskan diri sendiri yang mana itu tak dibutuhkan.\n\nKomentar juga dipakai untuk mengotodokumentasikan tool seperti JSDoc3: mereka membacanya dan menggenerate HTML-docs (atau docs di format lainnya).\n"
  },
  {
    "path": "1-js/03-code-quality/04-ninja-code/article.md",
    "content": "# Kode ninja\n\n\n```quote author=\"Confucius (Analects)\"\nLearning without thought is labor lost; thought without learning is perilous.\n```\n\nNinja programmer dari masa lalu memakai trik ini untuk mempertajam pikiran maintainer kode.\n\nCode review guru mencari-cari mereka dalam tugas pengujian.\n\nPengembang pemula kadang memakai mereka lebih baik dari ninja programmer.\n\nBaca mereka dengan hati-hati dan cari tahu siapa kamu -- ninja, pemula, atau mungkin code reviewer?\n\n\n```warn header=\"Ironi terdeteksi\"\nBanyak yang mencoba mengikuti jalan ninja. Tapi cuma sedikit yang sukses.\n```\n\n\n## Singkat adalah jiwa kecerdasan\n\nBuat kode sependek mungkin. Tunjukkan kepintaranmu.\n\nBiarkan fitur bahasa halus memandumu.\n\nMisalnya, perhatikan operator ternary ini `'?'`:\n\n```js\n// diambil dari library javascript terkenal\ni = i ? i < 0 ? Math.max(0, len + i) : i : 0;\n```\n\nKeren, kan? Kalau kamu menulis begitu, pengembang yang melihat baris ini dan mencoba memahami apa nilai `i` akan merasa senang. Lalu datanglah kamu, mencari jawaban.\n\nKatakan pada mereka bahwa yang lebih pendek selalu lebih baik that. Inisasikan mereka ke dalam langkah ninja.\n\n## Variabel satu-huruf\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe Dao hides in wordlessness. Only the Dao is well begun and well\ncompleted.\n```\n\nCara lain untuk mengkode cepat ialah memakai nama variabel huruf-tunggal di manapun. Seperti `a`, `b` atau `c`.\n\nVariabel pendek hiland dalam kode seperti ninja sungguhan di hutan. Tak ada yang mampu mencarinya memakai \"pencari\" editor. Dan bahkan jika seseorang menemukannya, mereka tak mampu men\"decipher\" arti nama `a` atau `b`.\n\n...Tapi ada pengecualian. Ninja riil tak akan pernah memakai `i` sebagai counter di loop `\"for\"`. Di tempat lain iya, tapi di sini tidak. Lihat saja, ada banyak huruf exotis. Misalnya, `x` atau `y`.\n\nVariabel exotis sebagai counter loop itu keren terutama jika badan loop memakan 1-2 laman (buat ia lebih panjang lagi jika kamu bisa). Lalu jika ada orang lihat lebih dalam ke loop, mereka tak akan cepat menerka nama variabel `x` sebagai counter loop.\n\n## Gunakan singkatan\n\nJika aturan tim melarang penggunaan nama satu-huruf dan geje -- perpendek mereka, buat singkatan.\n\nSeperti ini:\n\n- `list` -> `lst`.\n- `userAgent` -> `ua`.\n- `browser` -> `brsr`.\n- ...etc\n\nHanya orang-orang yang memiliki intuisi bagus yang akan mengerti nama-nama seperti itu. Cobalah untuk memendekan segalanya. Hanya orang-orang yang layak yang bisa bertahan dengan perkembangan kodemu.\n\n## Terbang Tinggi. Jadilah abstrak.\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe great square is cornerless<br>\nThe great vessel is last complete,<br>\nThe great note is rarified sound,<br>\nThe great image has no form.\n```\n\nSaat memilih nama cobalah pakai kata paling abstrak. Seperti `obj`, `data`, `value`, `item`, `elem` dan seterusnya.\n\n- **Nama ideal untuk variabel ialah `data`.** Pakai itu di manapun kamu bisa. Jelas, tiap variabel memegang *data*, kan?\n\n    ...Tapi bagaimana jika `data` sudah diambil? Coba `value`, itu juga universal. Setelahnya, sebuah variabel akhirnya mendapatkan sebuah *nilai*.\n\n- **Beri nama variabel berdasarkan tipe: `str`, `num`...**\n\n    Coba saja mereka. Pemula mungkin heran -- apa nama-nama begini berguna bagi ninja? Tentu!\n\n    Pasti, nama variabel masih punya arti. Ia menerangkan apa isi variabel: string, number atau yang lain. Tapi saat orang asing mencoba memahami kodenya, mereka akan terkejut melihat tak ada informasi sama sekali! Dan pasti akan gagal mengubah kodemu yang sudah dipikirkan matang-matang.\n\n    Tipe nilai mudah dicaritahu dari debugging. Tapi apa arti dari variabel? String/number mana yang disimpan?\n\n    Tak ada cara mencaritahu tanpa meditasi yang bagus!\n\n- **...Tapi bagaimana jika tak ada name lagi?** Tambahkan saja angka: `data1, item2, elem5`...\n\n## Uji perhatian\n\nHanya programmer sungguhan bisa memahami kodemu. Tapi bagaimana mengeceknya?\n\n**Salah satu caranya -- pakai nama variabel yang serupa, seperti `date` dan `data`.**\n\nCampurkan saja mereka sebisamu.\n\nBaca cepan kode begini makin mustahil. Dan saat ada typo... Ummm... Kita stuck cukup lama, waktunya minum teh.\n\n\n## Synonym pintar\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe Tao that can be told is not the eternal Tao. The name that can be named is not the eternal name.\n```\n\nMemakai nama *serupa* untuk hal *sama* membuat hidup menarik dan menampilkan kreatifitasmu ke publik.\n\nMisalnya, pikirkan prefix fungsi. Jika fungsi menunjukkan pesan ke layar -- mulai dengan `display…`, seperti `displayMessage`. Lalu jika fungsi lain menampilkan yang lain di layar, seperti nama pengguna, mulai dengan `show…` (seperti `showName`).\n\nMenyindir bahwa ada perbedaan halus antara kedua fungsi, padahal tidaka da.\n\nBuatlah sebuah team kawan ninja: jika John mulai *menampilkan* fungsi dengan `display...` didalam kodenya, lalu Peter menggunakan `render...`, dan Ann -- `paint...`. Perhatikan akan seberapa menarik dan berbedakah nantinya.\n\n...Dan sekarang untuk hat trick!\n\nUntuk dua fungsi dengan perbedaan mencolok -- pakai prefix yang sama!\n\nMisalnya, fungsi `printPage(page)` akan memakai printer. Dan fungsi `printText(text)` akan menaruh teks ke layar. Biarkan pembaca asing berpikir baik tentang fungsi bernama `printMessage`: \"Ke mana ia taruh pesannya? Ke printer atau ke layar?\". Supaya lebih bersinar, `printMessage(message)` sebaiknya mengoutput itu di jendela baru!\n\n## Pakai-ulang nama\n\n```quote author=\"Laozi (Tao Te Ching)\"\nOnce the whole is divided, the parts<br>\nneed names.<br>\nThere are already enough names.<br>\nOne must know when to stop.\n```\n\nTambah variabel baru hanya saat diperlukan.\n\nLebih baik gunakan ulang nama yang sudah ada. Tulis saja nilai baru ke dalamnya.\n\nDi dalam fungsi cobalah hanya memakai variabel yang dioper sebagai parameter.\n\nItu akan menyulitkan identifikasi apa yang ada di variabel *sekarang*. Dan juga darimana asalnya. Tujuannya untuk mengembangka intuisi dan memori orang yang membaca kodenya. Orang dengan intuisi lemah akan menganalisa kodenya baris per-baris dan menjejak perubahan ke seluruh cabang kode.\n\n**Varian canggih dari pendekatan ini ialah mengganti diam-diam (!) nilai dengan sesuatu yang serupa di tengah loop atau fungsi.**\n\nMisalnya:\n\n```js\nfunction ninjaFunction(elem) {\n  // 20 baris kode berjalan dengan elem\n\n  elem = clone(elem);\n\n  // 20 baris lagi, sekarang berjalan dengan clone dari elem!\n}\n```\n\nSobat programmer yang mau bekerja dengan `elem` di pertengahan kedua dari fungsi akan terkejut... Cuma selama debugging, setelah memeriksa kodenya mereka akan menemukan bahwa mereka bekerja dengan clone!\n\nTerlihat dalam kode secara reguler. Benar-benar efektif bahkan melawan ninja berpengalaman.\n\n## Underscore untuk kesenangan\n\nTaruh underscores `_` dan `__` sebelum nama variabel. Seperti `_name` atau `__value`. Akan lebih bagus jika cuma kamu yang tahu artinya. Atau, lebih baik, tambahkan mereka hanya untuk kesenangan, tanpa ada arti sama sekali. Atau arti berbeda di tempat berbeda.\n\nKamu membunuh dua kelinci satu tembakan. Pertama, kodenya jadi lebih panjang dan kurang terbaca, dan kedua, sobat pengembang menghabiskan banyak waktu mencaritahu arti underscores.\n\nNinja pintar menaruh underscore di satu spot kode dan menghindari mereka di tempat lain. Itu membuat kodenya jadi lebih rapuh dan meningkatkan kemungkinan muncul galat masa depan.\n\n## Tunjukkan cintamu\n\nBiarkan orang melihat betapa indahnya entiti kamu! Nama seperti `superElement`, `megaFrame` dan `niceItem` pasti akan mencerahkan pembacamu.\n\nTentu, di satu sisi, satu hal tertulis: `super..`, `mega..`, `nice..` Tapi di sisi lain -- ia tak membawa detil. Pembaca mungkin memutuskan untuk melihat arti tersembunyi dan meditasi selama sejam atau dua jam dari waktu kerja mereka.\n\n\n## Tumpang-tindih variabel terluar\n\n```quote author=\"Guan Yin Zi\"\nWhen in the light, can't see anything in the darkness.<br>\nWhen in the darkness, can see everything in the light.\n```\n\nPakai nama yang sama untuk variabel di dalam dan di luar fungsi. Simpelnya. Tak perlu usaha menemukan nama baru.\n\n```js\nlet *!*user*/!* = authenticateUser();\n\nfunction render() {\n  let *!*user*/!* = anotherValue();\n  ...\n  ...many lines...\n  ...\n  ... // <-- programmer mau bekerja dengan pengguna di sini dan...\n  ...\n}\n```\n\nProgrammer yang lompat ke dalam `render` mungkin akan gagal melihat ada `user` lokal yang membayangi user yang terluar.\n\nLalu mereka akan mencoba bekerja dengan `user` dengan asumsi ia variabel external, hasil `authenticateUser()`... Jebakan muncul! Halo, debugger...\n\n\n## Efek-samping di manapun!\n\nAda fungsi yang kelihatan tak mengubah apapun. Seperti `isReady()`, `checkPermission()`, `findTags()`... Mereka dikira melakukan kalkulasi, mencari dan menghasilkan data, tanpa mengubah apapun di luar mereka. Dengan kata lain, tanpa \"efek-samping\".\n\n**Trik yang sangat cantik ialah menambah aksi \"berfaedah\" ke mereka, selain tugas utamanya.**\n\nExpresi linglung kaget dari muka kolegamu saat mereka melihat fungsi bernama `is..`, `check..` atau `find...` yang mengubah sesuatu -- pastinya akan memperluas batas alasanmu.\n\n**Cara lain membuat kejutan ialah mengembalikan hasil non-standar.**\n\nTunjukan pemikiran orisinilmu! Biarkan panggilan `checkPermission` mengembalikan bukan `true/false`, tapi objek rumit dengan hasil pengecekan.\n\nPengembang itu yang mencoba `if (checkPermission(..))`, akan heran kenapa itu tak bekerja. Katakan ke mereka: \"Baca docs!\". Dan berikan artikel ini.\n\n\n## Fungsi yang kuat!\n\n```quote author=\"Laozi (Tao Te Ching)\"\nThe great Tao flows everywhere,<br>\nboth to the left and to the right.\n```\n\nJangan batasi fungsi karena tulisan namanya. Melebarlah.\n\nMisalnya, fungsi `validateEmail(email)` bisa (selain mengecek keebenaran email) menampilkan pesan galat dan meminta masukan ulang email.\n\nAksi tambahan sebaiknya jangan diperjelas dari nama fungsi. Coder ninja sejati akan membuat mereka tidak jelas dari kodenya juga.\n\n**Menggabung beberapa aksi jadi satu melindungi kodemu dari penggunaan ulang.**\n\nBayangkan, pengembang lain mau mengecek email, dan tak menghasilkan pesan apapun. Fungsimu  `validateEmail(email)` yang melakukan keduanya tak akan cocok dengan mereka. Jadi mereka tak akan mengganggu meditasimu dengan menanyakan itu.\n\n## Kesimpulan\n\nSemua \"potongan saran\" di atas berasal dari real kode sungguhan... Kadang, tertulis dari pengembang berpengalaman. Bahkan lebih berpengalaman dari kamu ;)\n\n- Ikuti beberapa dari mereka, dan kodemu akan menjadi penuh kejutan.\n- Ikuti banyak dari mereka, dan kodemu akan menjadi milikmu sepenuhnya, tak ada yang mau mengubahnya.\n- Ikuti semua, dan kodemu akan menjadi pelajaran berharga untuk pengembang muda yang mencari pencerahan.\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/3-pow-test-wrong/solution.md",
    "content": "Tes ini mendemonstrasikan satu dari godaan yang pengembang temui dan saat menulis tes.\n\nApa yang kita punya di sini sebenarnya 3 tes, tapi diletakkan sebagai satu fungsi tunngal dengan 3 assert.\n\nKadang lebih mudah menulis di cara ini, tapi jika muncul galat, kurang jelas apa yang salah.\n\nJika galat terjadi di tengah alur exekusi rumit, maka kita harus caritahu data di poin itu. Kita sebenarnya harus *mendebug tes*.\n\nAkan jauh lebih baik memecah tes jadi beberapa blok `it` dengan input dan output yang tertulis jelas.\n\nSeperti ini:\n```js\ndescribe(\"Raises x to power n\", function() {\n  it(\"5 in the power of 1 equals 5\", function() {\n    assert.equal(pow(5, 1), 5);\n  });\n\n  it(\"5 in the power of 2 equals 25\", function() {\n    assert.equal(pow(5, 2), 25);\n  });\n\n  it(\"5 in the power of 3 equals 125\", function() {\n    assert.equal(pow(5, 3), 125);\n  });\n});\n```\n\nKita mengganti `it` tunggal dengan `describe` dan grup blok `it`. Sekarang jika sesuatu gagal kita akan lihat jelas apa datanya.\n\nJuga kita bisa mengisolasi tes tunggal dan menjalankannya dalam mode standalone dengan menulis `it.only` ketimbang `it`:\n\n\n```js\ndescribe(\"Raises x to power n\", function() {\n  it(\"5 in the power of 1 equals 5\", function() {\n    assert.equal(pow(5, 1), 5);\n  });\n\n*!*\n  // Mocha hanya akan menjalankan blok ini\n  it.only(\"5 in the power of 2 equals 25\", function() {\n    assert.equal(pow(5, 2), 25);\n  });\n*/!*\n\n  it(\"5 in the power of 3 equals 125\", function() {\n    assert.equal(pow(5, 3), 125);\n  });\n});\n```\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/3-pow-test-wrong/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Apa yang salah dalam tes ini?\n\nApa yang salah dalam tes `pow` di bawah?\n\n```js\nit(\"Raises x to the power n\", function() {\n  let x = 5;\n\n  let result = x;\n  assert.equal(pow(x, 1), result);\n\n  result *= x;\n  assert.equal(pow(x, 2), result);\n\n  result *= x;\n  assert.equal(pow(x, 3), result);\n});\n```\n\nP.S. Secara sintaktis tesnya benar dan lulus.\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/article.md",
    "content": "# Pengetesan terotomasi dengan Mocha\n\nPengetesan terotomasi akan dipakai di tugas lebih lanjut, dan juga luas dipakai di proyek riil.\n\n## Kenapa kita butuh tes?\n\nSaat kita menulis fungsi, kita biasanya akan membayangkan apa yang ia harus lakukan: parameter apa memberikan hasil apa.\n\nSelama pengembangan, kita bisa mengecek fungsi dengan menjalankannya dan membandingkan keluaran yang muncul dengan yang keluaran diharapkan. Misalnya, kita bisa melakukannya di konsol.\n\nJika sesuatu buruk terjadi -- maka kita membetulkan kode, menjalankan lagi, mengecek hasil -- dan begitu terus hingga bekerja.\n\nTapi proses \"jalan-ulang\" manual seperti ini tak sempurna.\n\n**Ketika pengetesan kode dengan jalan-ulang manual, sangat rentang untuk kelupaan sesuatu.**\n\nMisalnya, kita membuat fungsi `f`. Membuat beberapa kode, mengetes: `f(1)` bekerja, tapi `f(2)` tak bekerja. Kita betulkan kodenya dan sekarang `f(2)` bekerja. Sudah lengkap? Tapi kita lupa mengetes-ulang `f(1)`. Di situ mungkin terjadi galat.\n\nIni sangat tipikal. Saat kita mengembangkan sesuatu, kita menyimpan banyak use case di kepala. Tapi sulit mengharapkan programmer mengecek semuanya secara manual setelah setiap perubahan. Jadi lebih mudah membetulkan satu hal dan merusak hal lainnya.\n\n**Pengecekan terotomasi artinya tes itu ditulis terpisah, sebagai tambahan ke kode. Mereka menjalankan kode kita dalam berbagai cara dan membandingkan hasil dengan harapan.**\n\n## Behavior Driven Development (BDD)\n\nAyo mulai dengan teknik bernama [Behavior Driven Development](http://en.wikipedia.org/wiki/Behavior-driven_development) atau, singkatnya, BDD.\n\n**BDD adalah tiga hal dalam satu: tes DAN dokumentasi DAN contoh.**\n\nUntuk memahami BDD, kita akan periksa kasus praktik pengembangan.\n\n## Pengembangan \"pow\": spek\n\nKatakan kita mau membuat fungsi `pow(x, n)` yang menaikkan `x` ke bilangan pangkat `n`. Kita asumsikan `n≥0`.\n\nTugas itu cuma contoh: ada operator `**` di JavaScript yang bisa melakukan itu, tapi di sini kita koncentrasi di alur pengembangan yang bisa ditiru di tugas komplex lainnya juga.\n\nSebelum membuat kode `pow`, kita bisa bayangkan apa yang harus dilakukan fungsi itu dan menjelaskannya.\n\nDeskripsi begitu disebut _spesifikasi_ atau, singkatnya, spek, dan berisi deskripsi use case bersama dengan tes untuk mereka, seperti ini:\n\n```js\ndescribe(\"pow\", function () {\n  it(\"raises to n-th power\", function () {\n    assert.equal(pow(2, 3), 8);\n  });\n});\n```\n\nSpek punya tiga blok bangunan utama yang bisa kamu lihat di bawah:\n\n`describe(\"title\", function() { ... })` : Fungsionalitas apa yang kita jelaskan. Di kasus ini kita akan menjelaskan fungsi `pow`. Dipakai untuk mengelompokkan \"pekerja\" -- blok `it`.\n\n`it(\"use case description\", function() { ... })` : Di judul `it` kita _dalam bahasa manusia_ menjelaskan use case tertentu, dan argumen kedua ialah fungsi yang mengetes itu.\n\n`assert.equal(value1, value2)` : Kode di dalam blok `it`, jika implementasinya benar, harus berjalan tanpa galat.\n\n    Fungsi `assert.*` dipakai untuk mengecek apakah `pow` bekerja sesuai harapan. Tepat di sini kita memakai salah satunya -- `assert.equal`, ia membandingkan argumen dan menghasilkan galat jika mereka tak sama. Di sini ia mengecek apakah hasil `pow(2, 3)` sama dengan `8`. Ada tipe perbandingan dan pengecekan lain, yang akan kita tambah nanti.\n\nSpesifikasi ini bisa diexekusi, dan ia akan menjalankan tes yang dispesifikasi dalam blok `it`. Kita akan lihat nanti.\n\n## Alur pengembangan\n\nAlur pengembangan biasanya seperti ini:\n\n1. Spek inisial ditulis, dengan tes untuk kebanyakan fungsionalitas dasar.\n2. Implementatsi inisial dibuat.\n3. Untuk mengecek apakah ia bekerja, kita jalankan framework pengetesan [Mocha](http://mochajs.org/) (detil lebih segera) yang menjalankan spek. Saat fungsionalitas tak lengkap, galat ditampilkan. Kita buat koreksi hingga semuanya bekerja.\n4. Sekarang kita punya implementasi inisial yang bekerja dengan tes.\n5. Kita tambah use case lain ke spek, mungkin belum didukung implementasinya. Tes mulai gagal.\n6. Pergi ke 3, perbaharui implementasinya hingga tes tak memberikan galat.\n7. Ulangi langkah 3-6 hingga fungsionalitasnya siap.\n\nJadi, pengembangannya _iteratif_. Kita tulis spek, implementasikan, memastikan tes lulus, lalu menulis tes lainnya, memastikan mereka bekerja dll. Akhirnya kita punya implementasi yang bekerja dan tes untuk itu.\n\nAyo lihat alur pengembangan ini di kasus praktik kita.\n\nLangkap pertama sudah lengkap: kita punya spek inisial untuk `pow`. Sekarang, sebelum membuat implementasinya, ayo pakai beberapa librari JavaScript untuk menjalankan tes, hanya untuk melihat mereka bekerja (mereka semua akan gagal).\n\n## Spec dalam aksi\n\nDi sini di tutorial ini kita akan memakai librari JavaScript untuk tes:\n\n- [Mocha](http://mochajs.org/) -- inti framework: menyediakan fungsi testing umum `describe` dan `it` dan fungsi utama yang menjalankan test.\n- [Chai](http://chaijs.com) -- librari dengan banyak penambahan. Ini membuatmu bisa untuk menggunakan banyak penambahan yang berbeda, untuk sekarang kita hanya membutuhkan `assert.equal`.\n- [Sinon](http://sinonjs.org/) -- sebuah librari untuk memata-matai fungsi, meniru fungsi bawaan dan lainnya, kita akan butuh ini nanti.\n\nLibrari ini cocok baik untuk pengetesan in-browser dan server-side. Di sini kita akan mempertimbangkan varian peramban.\n\nLaman HTML lengkap dengan framework ini dan spek `pow`:\n\n```html src=\"index.html\"\n\n```\n\nLaman ini bisa dibagi jadi lima bagian:\n\n1. `<head>` -- menambah librari dan gaya pihak-ketiga untuk tes.\n2. `<script>` dengan fungsi untuk dites, di kasus kita -- degngan kode untuk `pow`.\n3. Tes -- di kasus kita script external `test.js` yang punya `describe(\"pow\", ...)` dari atas.\n4. Elemen HTML `<div id=\"mocha\">` akan dipakai untuk mengoutputkan hasil.\n5. Pengetesan dimulai dari command `mocha.run()`.\n\nHasilnya:\n\n[iframe height=250 src=\"pow-1\" border=1 edit]\n\nUntuk sekarang, test gagal, ada error. Disitulah logikanya: kita mempunyai sebuah fungsi kosong didalam kode `pow`, jadi `pow(2,3)` mengembalikan `undefined` daripada `8`.\n\nUntuk dimasa mendatang, catat baik-baik akan ada penjalan test tingkat tinggi, seperti [karma](https://karma-runner.github.io/) dan lainnya, yang akan memudahkan kita untuk mengotomasi berbagai macam test.\n\n## Implementasi inisial\n\nAyo buat implementasi sederhana dari `pow`, supaya tes lulus:\n\n```js\nfunction pow(x, n) {\n  return 8; // :) we cheat!\n}\n```\n\nWow, sekarang itu bekerja!\n\n[iframe height=250 src=\"pow-min\" border=1 edit]\n\n## Mengimprov spek\n\nApa yang sudah kita lakukan itu pasti curang. Fungsinya tak akan bekerja: percobaan untuk mengkalculasi `pow(3,4)` akan memberi hasil salah, tapi tes lulus.\n\n...Tapi situasi ini agak tipikal, ia terjadi dalam latihan. Tes lulus, tapi fungsinya salah bekerja. Spek kita tak sempurna. Kita harus menambah use case lagi ke situ.\n\nAyo tambah satu tes lagi untuk mengecek bahwa `pow(3, 4) = 81`.\n\nKita bisa pilih salah satu cara mengorganisasikan tes di sini:\n\n1. Varian pertama -- tambah satu lagi `assert` ke dalam `it` yang sama:\n\n   ```js\n   describe(\"pow\", function() {\n\n     it(\"raises to n-th power\", function() {\n       assert.equal(pow(2, 3), 8);\n   *!*\n       assert.equal(pow(3, 4), 81);\n   */!*\n     });\n\n   });\n   ```\n\n2. Kedua -- buat dua tes:\n\n   ```js\n   describe(\"pow\", function () {\n     it(\"2 raised to power 3 is 8\", function () {\n       assert.equal(pow(2, 3), 8);\n     });\n\n     it(\"3 raised to power 4 is 81\", function () {\n       assert.equal(pow(3, 4), 81);\n     });\n   });\n   ```\n\nPerbedaan prinsipal ialah saat `assert` memicu galat, blok `it` segera berhenti. Jadi, di varian pertama jika `assert` pertama gagal, maka kita takkan pernah melihat hasil `assert` kedua.\n\nMemisahkan tes berguna untuk mendapat informasi lebih tentang apa yang terjadi, jadi varian kedua lebih baik.\n\nDan selain itu, ada satu lagi aturan yang baik untuk diikuti.\n\n**Satu tes mengecek satu hal.**\n\nJika kita lihat dan melihat dua pengecekan independen di dalamnya, lebih baik memecah itu menjadi dua pengecekan yang lebih simpel.\n\nJadi ayo kita lanjut dengan varian kedua.\n\nHasilnya:\n\n[iframe height=250 src=\"pow-2\" edit border=\"1\"]\n\nSeperti yang kita harapkan, tes kedua gagal. Jelas, fungsi kita selalu mengembalikan `8`, sedangkan `assert` mengharapkan `81`.\n\n## Mengimprov implementasi\n\nAyo tulis sesuatu yang lebih riil supaya tes lulus:\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nUntuk yakin bahwa fungsi bekerja dengan baik, ayo kita tes dia dengan lebih banyak nilai. Daripada menulis blok `it` secara manual, kita bisa menggenerate mereka di `for`:\n\n```js\ndescribe(\"pow\", function () {\n  function makeTest(x) {\n    let expected = x * x * x;\n    it(`${x} in the power 3 is ${expected}`, function () {\n      assert.equal(pow(x, 3), expected);\n    });\n  }\n\n  for (let x = 1; x <= 5; x++) {\n    makeTest(x);\n  }\n});\n```\n\nHasilnya:\n\n[iframe height=250 src=\"pow-3\" edit border=\"1\"]\n\n## Deskripsi bersarang\n\nKita akan menambah lebih banyak tes. Tapi sebelum itu mari ingat bahwa fungsi pembantu `makeTest` dan `for` harus digrup bersama. Kita tak akan butuh `makeTest` di tes lain, ia dibutuhkan hanya di `for`: tugas umum mereka ialah mengecek bagaimana `pow` naik ke pangkat yang diberikan.\n\nPengelompokkan selesai dengan `describe` bersarang:\n\n```js\ndescribe(\"pow\", function() {\n\n*!*\n  describe(\"raises x to power 3\", function() {\n*/!*\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n*!*\n  });\n*/!*\n\n  // ... banyak tes untuk diikuti di sini, baik describe dan it bisa ditambah\n});\n```\n\n`describe` bersarang mendefinisikan tes \"subgroup\" baru. Di output baru kita bisa lihat indentasi berjudul:\n\n[iframe height=250 src=\"pow-4\" edit border=\"1\"]\n\nDi masa depan kita bisa menambah lebih banyak `it` dan `describe` di level tertinggi dengan fungsi pembantu mereka sendiri, mereka takkan melihat `makeTest`.\n\n````smart header=\"`before/after`dan`beforeEach/afterEach`\" Kita bisa mengeset fungsi `before/after`yang berjalan sebelum/setelah menjalankan tes, dan juga fungsi`beforeEach/afterEach`yang berjalan sebelum/setelah *setiap*`it`.\n\nMisalnya:\n\n```js no-beautify\ndescribe(\"test\", function () {\n  before(() => alert(\"Testing started – before all tests\"));\n  after(() => alert(\"Testing finished – after all tests\"));\n\n  beforeEach(() => alert(\"Before a test – enter a test\"));\n  afterEach(() => alert(\"After a test – exit a test\"));\n\n  it(\"test 1\", () => alert(1));\n  it(\"test 2\", () => alert(2));\n});\n```\n\nSequence yang bekerja akan jadi:\n\n```\nTes dimulai – sebelum semua tes (before)\nSebelum tes – masukkan tes (beforeEach)\n1\nSetelah tes – keluar tes (afterEach)\nSebelum tes – masukkan tes (beforeEach)\n2\nSetelah tes – keluar tes (afterEach)\nTes selesai – setelah semua tes (after)\n```\n\n[edit src=\"beforeafter\" title=\"Buka contoh di sandbox.\"]\n\nBiasanya, `beforeEach/afterEach` dan `before/after` dipakai untuk melakukan inisialisasi, mengnolkan counter atau melakukan sesuatu di antara tes (atau grup tes).\n\n````\n\n## Mengextend spek\n\nFungsional dasar `pow` sudah lengkap. Iterasi pertama pengembangan sudah selesai. Saat kita selesai merayakan dan minum champagne -- ayo kita lanjut dan mengimprovisasinya.\n\nSeperti yang tadi dikatakan, fungsi `pow(x, n)` dimaksudkan untuk bekerja dengan nilai integer positif `n`.\n\nUntuk mengindikasikan galat matematis, fungsi JavaScript biasanya mengembalikan `NaN`. Ayo lakukan hal serupa untuk nilai invalid `n`.\n\nAyo mulai tambah kelakuan tersebut ke spek(!):\n\n```js\ndescribe(\"pow\", function() {\n\n  // ...\n\n  it(\"untuk n negative hasilnya NaN\", function() {\n*!*\n    assert.isNaN(pow(2, -1));\n*/!*\n  });\n\n  it(\"untuk n non-integer hasilnya NaN\", function() {\n*!*\n    assert.isNaN(pow(2, 1.5));\n*/!*\n  });\n\n});\n```\n\nHasilnya dengan tes baru:\n\n[iframe height=530 src=\"pow-nan\" edit border=\"1\"]\n\nTes yang baru ditambahkan gagal, karena implementasi kita tak mendukung mereka. Itu cara BDD selesai: pertama kita tulis tes yang gagal, lalu buat implementasi untuk mereka.\n\n```smart header=\"Assersi lain\"\nTolong catat assersi `assert.isNaN`: ia melakukan ecek terhadap `NaN`.\n\nAda assersi lain di [Chai](http://chaijs.com) juga, misalnya:\n\n- `assert.equal(value1, value2)` -- mengecek ekualitas  `value1 == value2`.\n- `assert.strictEqual(value1, value2)` -- mengecek ekualitas ketat `value1 === value2`.\n- `assert.notEqual`, `assert.notStrictEqual` -- menginversi cek salah satu di atas.\n- `assert.isTrue(value)` -- mengecek apa `value === true`\n- `assert.isFalse(value)` -- mengecek apa `value === false`\n- ...daftar lengkapnya ada di [docs](http://chaijs.com/api/assert/)\n```\n\nJadi kita harus tambah beberapa baris `pow`:\n\n```js\nfunction pow(x, n) {\n*!*\n  if (n < 0) return NaN;\n  if (Math.round(n) != n) return NaN;\n*/!*\n\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nSekarang itu bekerja, semua tes lulus:\n\n[iframe height=300 src=\"pow-full\" edit border=\"1\"]\n\n[edit src=\"pow-full\" title=\"Open the full final example in the sandbox.\"]\n\n## Kesimpulan\n\nDalam BDD, spek mulai pertama, diikuti implementasi. Di akhir kita punya baik spek maupun kode.\n\nSpek bisa dipakai dalam tiga cara:\n\n1. Sebagai **Tes** - mereka menjamin kode bekerja dengan benar.\n2. Sebagai **Docs** -- judul `describe` dan `it` menjelaskan apa yang dilakukan fungsi.\n3. Sebagai **Contoh** -- tes sebenarnya contoh kerja yang menjelaskan bagaimana fungsi bisa dipakai.\n\nDengan spek, kisa bisa secara aman mengimprov, mengubah, bahkan menulis-ulang fungsi dari nol dan memastikan ia masih bekerja dengan baik.\n\nIni terutama penting di proyek besar saat fungsi dipakai di banyak tempat. Saat kita mengubah fungsi begini, tak ada cara untuk mengecek secara manual jika tiap tempat yang menggunakannya masih bekerja dengan baik.\n\nTanpa tes, orang punya dua cara:\n\n1. Untuk melakukan perubahan the change, no matter what. And then our users meet bugs, as we probably fail to check something manually.\n2. Atau, jika hukuman untuk galat itu berat, karena tak ada tes, orang menjadi takut memodifikasi fungsi seperti ini, lalu kode menjadi tak mutakhir, tak ada yang mau meneruskannya. Tidak baik untuk pengembangan.\n\n**Pengetesan otomatis membantu menghindari masalah ini!**\n\nJika proyek ini dibahas dengan tes, harusnya tak masalah. Tiap ada perubahan, kita bisa jalankan tes dan melihat banyak pengecekan yang dibuat dengan cepat.\n\n**Selain itu, kode oto-tes punya arsitektur lebih baik.**\n\nAlaminya, itu karena kode oto-tes lebih mudah dimodifikasi dan diimprov. Tapi ada juga alasan lain.\n\nUntuk menulis tes, kode harus diorganisir sedemikian rupa di mana tiap fungsi punya tugas jelas, input dan output yang terdefinisi dengan baik. Artinya satu arsitektur yang bagus dari awal.\n\nDi kehidupan riil kadang tak mudah. Kadang sulit menulis spek sebelum kode aktual, karena belum jelas bagaimana ia harus bersikap. Tapi umumnya menulis tes membuat pengembangan lebih cepat dan lebih stabil.\n\nNanti di tutorial ini kamu akan menemui banyak tugas dengan tes yang matang. Jadi kamu akan melihat banyak contoh praktik.\n\nMenulis tes membutuhkan pengetahuan JavaScript yang baik. Tapi kita baru saja mulai belajar. Jadi, supaya settle semuanya, dari sekarang kamu belum wajib menulis tes, tapi kamu harus sudah bisa membaca mereka meski mereka agak rumit dari yang ada di bab ini.\n````\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/beforeafter.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <!-- Tambahkan mocha css untuk menampilkan hasil. -->\n    <link\n      rel=\"stylesheet\"\n      href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\"\n    />\n    <!-- Tambahkan kode kerangka kerja/framework mocha -->\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n    <script>\n      // Mengaktifkan pengujian dengan bdd-style\n      mocha.setup(\"bdd\");\n    </script>\n    <!-- Tambahkan chai script -->\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n    <script>\n      // Chai memiliki banyak fitur, mari kita membuat assert menjadi global.\n      let assert = chai.assert;\n    </script>\n  </head>\n\n  <body>\n    <!-- Skrip dengan pengujian (describe, it...). -->\n    <script src=\"test.js\"></script>\n    <!-- Elemen dengan id=\"mocha\" akan berisi hasil uji coba/test. -->\n    <div id=\"mocha\"></div>\n\n    <!-- Jalankan pengujian/tests -->\n    <script>\n      mocha.run();\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/beforeafter.view/test.js",
    "content": "describe(\"test\", function() {\n  \n   // Mocha usually waits for the tests for 2 seconds before considering them wrong\n  \n  this.timeout(200000); // With this code we increase this - in this case to 200,000 milliseconds\n\n  // This is because of the \"alert\" function, because if you delay pressing the \"OK\" button the tests will not pass!\n  \n  before(() => alert(\"Testing started – before all tests\"));\n  after(() => alert(\"Testing finished – after all tests\"));\n\n  beforeEach(() => alert(\"Before a test – enter a test\"));\n  afterEach(() => alert(\"After a test – exit a test\"));\n\n  it('test 1', () => alert(1));\n  it('test 2', () => alert(2));\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    mocha.setup('bdd'); // minimal setup\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      /* function code is to be written, empty now */\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-1.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      /* function code is to be written, empty now */\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-1.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"raises to n-th power\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-2.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      return 8; // :) we cheat!\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-2.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"2 raised to power 3 is 8\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n  it(\"3 raised to power 4 is 81\", function() {\n    assert.equal(pow(3, 4), 81);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-3.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-3.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  function makeTest(x) {\n    let expected = x * x * x;\n    it(`${x} in the power 3 is ${expected}`, function() {\n      assert.equal(pow(x, 3), expected);\n    });\n  }\n\n  for (let x = 1; x <= 5; x++) {\n    makeTest(x);\n  }\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-4.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-4.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  // ... more tests to follow here, both describe and it can be added\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-full.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      if (n < 0) return NaN;\n      if (Math.round(n) != n) return NaN;\n\n      let result = 1;\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-full.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  it(\"if n is negative, the result is NaN\", function() {\n    assert.isNaN(pow(2, -1));\n  });\n\n  it(\"if n is not integer, the result is NaN\", function() {\n    assert.isNaN(pow(2, 1.5));\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-min.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow() {\n      return 8; // :) we cheat!\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-min.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  it(\"raises to n-th power\", function() {\n    assert.equal(pow(2, 3), 8);\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-nan.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!-- add mocha css, to show results -->\n  <link rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.css\">\n  <!-- add mocha framework code -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/mocha/3.2.0/mocha.js\"></script>\n  <script>\n    // enable bdd-style testing\n    mocha.setup('bdd');\n  </script>\n  <!-- add chai -->\n  <script src=\"https://cdnjs.cloudflare.com/ajax/libs/chai/3.5.0/chai.js\"></script>\n  <script>\n    // chai has a lot of stuff, let's make assert global\n    let assert = chai.assert;\n  </script>\n</head>\n\n<body>\n\n  <script>\n    function pow(x, n) {\n      let result = 1;\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n      return result;\n    }\n  </script>\n\n  <!-- the script with tests (describe, it...) -->\n  <script src=\"test.js\"></script>\n\n  <!-- the element with id=\"mocha\" will contain test results -->\n  <div id=\"mocha\"></div>\n\n  <!-- run tests! -->\n  <script>\n    mocha.run();\n  </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "1-js/03-code-quality/05-testing-mocha/pow-nan.view/test.js",
    "content": "describe(\"pow\", function() {\n\n  describe(\"raises x to power 3\", function() {\n\n    function makeTest(x) {\n      let expected = x * x * x;\n      it(`${x} in the power 3 is ${expected}`, function() {\n        assert.equal(pow(x, 3), expected);\n      });\n    }\n\n    for (let x = 1; x <= 5; x++) {\n      makeTest(x);\n    }\n\n  });\n\n  it(\"if n is negative, the result is NaN\", function() {\n    assert.isNaN(pow(2, -1));\n  });\n\n  it(\"if n is not integer, the result is NaN\", function() {\n    assert.isNaN(pow(2, 1.5));\n  });\n\n});\n"
  },
  {
    "path": "1-js/03-code-quality/06-polyfills/article.md",
    "content": "# Polyfill dan transpiler\n\nJavaScript secara konsisten terus berevolusi. Proposal-proposal untuk menambah fitur-fitur baru terus bermunculan. Proposal-proposal ini akan didaftarkan pada <https://tc39.github.io/ecma262/> jika memang berpotensi dan layak untuk ditambahkan dalam standard dalam bahasa pemrograman JavaScript. Kemudian proposal-proposal yang telah diterima akan dimasukkan dalam [daftar spesifikasi](http://www.ecma-international.org/publications/standards/Ecma-262.htm) JavaScript.\n\nTim yang mengurus JavaScript mengerti dan akan mengusulkan mana dari proposal-proposal ini yang akan diimplementasikan terlebih dahulu. Tim ini boleh saja nanti memasukkan proposal-proposal ini kedalam kategori 'draft / dalam perancangan' atau 'postpone / tunda' karena mungkin menurut mereka proposal-proposal ini menarik untuk didiskusikan lebih dalam atau sulit untuk direalisasikan.\n\nSangat wajar jika kebanyakan dari browser-browser yang ada hanya mengimplementasikan bagian-bagian yang tidak terlalu sulit.\n\nSebuah halaman yang bagus untuk melihat kondisi terkini dari fitur yang didukung oleh bahasa ini ialah <https://kangax.github.io/compat-table/es6/> (isinya banyak, kita masih banyak yang belum dipelajari)\n\nSebagai programmer, kita suka untuk menggunakan fitur yang terbaru. lebih banyak fitur bagus - lebih baik lagi!\n\ndi sisi lain, bagaimana membuat kodingan modern bekerja di mesin yang lama yang tidak mengetahui fitur-fitur terbaru ?\n\nAda dua cara untuk itu:\n\n1. Transpilers.\n2. Polyfills.\n\ndi chapter ini, tujuan kita adalah untuk mendapatkan intisari cara kerjanya, dan tempatnya dalam proses pengembangan web.\n\n## Transpilers\n\nSebuah [transpiler](https://en.wikipedia.org/wiki/Source-to-source_compiler) adalah perangkat lunak khusus yang dapat mengurai (\"membaca dan memahami\") kode modern, dan menulis ulang menggunakan konstruksi sintaks yang lebih lama, sehingga hasilnya akan sama.\n\nMisalnya. JavaScript sebelum tahun 2020 tidak memiliki \"nullish coalescing operator\" `??`. Jadi, jika pengunjung menggunakan browser yang sudah ketinggalan zaman, ia mungkin gagal memahami kode seperti `height = height ?? 100`\n\nSebuah transpiler akan menganalisa kodingan kita dan menulis `height ?? 100` menjadi `(height !== undefined && height !== null) ? height : 100`.\n\n```js\n// sebelum menjalankan transpiler\nheight = height ?? 100;\n\n// setelah transpile dijalankan\nheight = (height !== undefined && height !== null) ? height : 100;\n```\n\nSekarang kode yang ditulis ulang cocok untuk mesin JavaScript lama.\n\nBiasanya, pengembang menjalankan transpiler di komputer mereka sendiri, dan kemudian menyebarkan kode yang ditranspilasi ke server.\n\nBerbicara tentang nama, [Babel](https://babeljs.io) adalah salah satu transpiler paling terkenal di luar sana. \n\nSistem pembangunan proyek modern, seperti [webpack](http://webpack.github.io/), menyediakan sarana untuk menjalankan transpiler secara otomatis pada setiap perubahan kode, sehingga sangat mudah untuk diintegrasikan ke dalam proses pengembangan.\n\n## Polyfills\n\nFitur bahasa baru tidak hanya mencakup konstruksi dan operator sintaks, tetapi juga fungsi bawaan.\n\nMisalnya, `Math.trunc (n)` adalah fungsi yang \"memotong\" bagian desimal dari sebuah angka, misalnya `Math.trunc (1.23) = 1`.\n\nDi beberapa mesin JavaScript (sangat usang), tidak ada `Math.trunc`, jadi kode seperti itu akan gagal.\n\nkarena kita berbicara tentang fungsi baru, bukan perubahan sintaks, tidak perlu mentranspilasi apa pun di sini. Kita hanya perlu mendeklarasikan fungsi yang hilang.\n\nSkrip yang memperbarui / menambahkan fungsi baru disebut \"polyfill\". Ini \"mengisi\" celah dan menambahkan implementasi yang hilang.\n\nUntuk kasus khusus ini, polyfill untuk `Math.trunc` adalah skrip yang mengimplementasikannya, seperti ini:\n\n```js\nif (!Math.trunc) { // kalo ga ada fungsi seperti ini\n  // implementasikan\n  Math.trunc = function(number) {\n    // Math.ceil dan Math.floor ada bahkan di mesin JavaScript yang lama\n    // mereka akan dibahas nanti di tutorial\n    return number < 0 ? Math.ceil(number) : Math.floor(number);\n  };\n}\n```\n\nJavaScript adalah bahasa yang sangat dinamis, skrip dapat menambah / memodifikasi fungsi apa pun, bahkan termasuk yang sudah ada di dalamnya.\n\nDua library polyfill yang menarik adalah:\n- [core js](https://github.com/zloirock/core-js) yang mendukung banyak hal, memungkinkan untuk kita memasukkan hanya fitur yang dibutuhkan.\n- [polyfill.io](http://polyfill.io) layanan yang menyediakan skrip dengan polyfills, bergantung pada fitur dan browser pengguna.\n\n\n## Kesimpulan\n\nDi bab ini, kami ingin memotivasi Anda untuk mempelajari fitur bahasa modern dan bahkan \"yang paling mutakhir\", meskipun fitur tersebut belum didukung dengan baik oleh mesin JavaScript.\n\nJangan lupa untuk menggunakan transpiler (jika menggunakan sintaks atau operator modern) dan polyfill (untuk menambahkan fungsi yang mungkin hilang). Dan mereka akan memastikan bahwa kodenya berfungsi.\n\nMisalnya, nanti saat Anda sudah terbiasa dengan JavaScript, Anda dapat menyiapkan sistem pembuatan kode berdasarkan [webpack](http://webpack.github.io/) dengan [babel-loader](https://github.com/babel/babel-loader).\n\nSumber daya bagus yang menunjukkan status dukungan saat ini untuk berbagai fitur:\n- <https://kangax.github.io/compat-table/es6/> - untuk JavaScript murni.\n- <https://caniuse.com/> - untuk fungsi terkait dengan browser.\n\nP.S. Google Chrome biasanya paling mutakhir dengan fitur bahasa, coba saja jika demo tutorial gagal. Sebagian besar demo tutorial berfungsi dengan browser modern apa pun."
  },
  {
    "path": "1-js/03-code-quality/index.md",
    "content": "# Kualitas Kode\n\nBab ini menjelaskan tentang *coding practices* yang akan kita selanjutnya gunakan di proses *development*.\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/2-hello-object/solution.md",
    "content": "\n\n```js\nlet user = {};\nuser.name = \"John\";\nuser.surname = \"Smith\";\nuser.name = \"Pete\";\ndelete user.name;\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/2-hello-object/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Hello, object\n\nTulis kode, satu baris untuk setiap aksi:\n\n1. Buatlah sebuah objek kosong `user`.\n2. Tambahkan properti `name` dengan nilai `John`.\n3. Tambahkan properti `surname` dengan nilai `Smith`.\n4. Ubah nilai `name` menjadi `Pete`.\n5. Hapus properti `name` dari objek.\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/_js.view/solution.js",
    "content": "function isEmpty(obj) {\n  for (let key in obj) {\n    // jika perulangan dimulai, berarti ada sebuah properti\n    return false;\n  }\n  return true;\n}\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/_js.view/test.js",
    "content": "describe(\"isEmpty\", function() {\n  it(\"returns true for an empty object\", function() {\n    assert.isTrue(isEmpty({}));\n  });\n\n  it(\"returns false if a property exists\", function() {\n    assert.isFalse(isEmpty({\n      anything: false\n    }));\n  });\n});"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/solution.md",
    "content": "Lakukanlah perulangan pada objek dan segera `return false` jika ada setidaknya satu properti"
  },
  {
    "path": "1-js/04-object-basics/01-object/3-is-empty/task.md",
    "content": "nilai penting: 5\n\n---\n\n# cek kekosongan\n\nTulislah sebuah fungsi `isEmpty(obj)` yang mana akan mengembalikan `true` jika objek tidak memiliki properti dan `false` jika sebaliknya.\n\nHarus bekerja seperti ini:\n\n```js\nlet schedule = {};\n\nalert( isEmpty(schedule) ); // true\n\nschedule[\"8:30\"] = \"get up\";\n\nalert( isEmpty(schedule) ); // false\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/5-sum-object/solution.md",
    "content": "\n```js run\nlet salaries = {\n  John: 100,\n  Ann: 160,\n  Pete: 130\n};\n\nlet sum = 0;\nfor (let key in salaries) {\n  sum += salaries[key];\n}\n\nalert(sum); // 390\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/5-sum-object/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tambahkan properti objek\n\nKita punya sebuah objek untuk menyimpan gaji dari tim kita:\n\n```js\nlet salaries = {\n  John: 100,\n  Ann: 160,\n  Pete: 130\n}\n```\n\nTulislah kode untuk menambahkan seluruh gaji dan simpanlah didalam sebuah variabel `sum`. Haruslah menjadi `390` di contoh diatas.\n\nJika `salaries` kosong, selanjutnya hasilnya haruslah `0`."
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/solution.js",
    "content": "function multiplyNumeric(obj) {\n  for (let key in obj) {\n    if (typeof obj[key] == 'number') {\n      obj[key] *= 2;\n    }\n  }\n}"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/source.js",
    "content": "let menu = {\n  width: 200,\n  height: 300,\n  title: \"My menu\"\n};\n\n\nfunction multiplyNumeric(obj) {\n  \n  /* Tulis kodemu disini */\n\n}\n\nmultiplyNumeric(menu);\n\nalert( \"menu width=\" + menu.width + \" height=\" + menu.height + \" title=\" + menu.title );\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/_js.view/test.js",
    "content": "describe(\"multiplyNumeric\", function() {\n  it(\"multiplies all numeric properties by 2\", function() {\n    let menu = {\n      width: 200,\n      height: 300,\n      title: \"My menu\"\n    };\n    let result = multiplyNumeric(menu);\n    assert.equal(menu.width, 400);\n    assert.equal(menu.height, 600);\n    assert.equal(menu.title, \"My menu\");\n  });\n\n  it(\"returns nothing\", function() {\n    assert.isUndefined( multiplyNumeric({}) );\n  });\n\n});\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/solution.md",
    "content": ""
  },
  {
    "path": "1-js/04-object-basics/01-object/8-multiply-numeric/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Kalikan properti numerik dengan 2\n\nBuatlah sebuah fungsi `multiplyNumerik(obj)` yang mengkalikan seluruh properti numerik dari `obj` dengan `2`.\n\nContoh:\n\n```js\n// sebelum dipanggil\nlet menu = {\n  width: 200,\n  height: 300,\n  title: \"My menu\"\n};\n\nmultiplyNumeric(menu);\n\n// setelah dipanggil\nmenu = {\n  width: 400,\n  height: 600,\n  title: \"My menu\"\n};\n```\n\nPerhatikan bahwa `multiplyNumeric` tidak butuh mengembalikan apapun. Fungsinya harus memodifikasi objectnya langsung.\n\nCatatan, gunakan `typeof` untuk memeriksa angka disini.\n\n\n"
  },
  {
    "path": "1-js/04-object-basics/01-object/article.md",
    "content": "\n# Objek\n\nSeperti yang kita tahu dari bab <info:types>, ada delapan tipe data di JavaScript. Enak dari mereka disebut \"primitif\", karena nilai mereka berisi cuma satu hal tunggal (entah string atau angka atau apapun).\n\nKontrasnya, objek dipakai untuk menyimpan koleksi terkunci dari berbagai data dan entitas rumit lainnya. Di JavaScript, objek menembus hampir tiap aspek bahasa. Jadi kita harus memahami mereka dulu sebelum masuk lebih dalam ke manapun.\n\nTiap objek bisa dibuat dengan tanda bracket `{…}` dengan daftar *properti* opsional. Properti ialah pasangan \"key: value\", di mana `key` string (juga disebut \"nama properti\"), dan `value` bisa apapun.\n\nKita bisa bayangkan objek sebagai kabinet dengan file bertanda. Tiap potong data disimpan di dalam filenya dengan kuncinya. Mudah mencari filenya dengan namanya atau menambah/menghapus satu file.\n\n![](object.svg)\n\nObjek kosong (\"kabinet kosong\") bisa dibuat memakai salah satu dari dua syntax:\n\n```js\nlet user = new Object(); // syntax \"konstruktor objek\"\nlet user = {};  // syntax \"literal objek\"\n```\n\n![](object-user-empty.svg)\n\nBiasanya, tanda bracket `{...}` dipakai. Deklarasi itu disebut *literal objek*.\n\n## Literal dan properti\n\nKita bisa segera menaruh beberapa properti ke dalam `{...}` sebagai pasangan \"key: value\":\n\n```js\nlet user = {     // objek\n  name: \"John\",  // dengan kunci \"name\" menyimpan nilai \"John\"\n  age: 30        // dengan kunci \"age\" menyimpan nilai 30\n};\n```\n\nProperti punya kunci (juga disebut \"nama\" atau \"identifier\") sebelum colon `\":\"` dan nilai di sebelah kanannya.\n\nDalam objek `user`, ada dua properti:\n\n1. Properti pertama punya nama `\"name\"` dan nilai `\"John\"`.\n2. Yang kedua punya nama `\"age\"` dan nilai `30`.\n\nHasil objek `user` bisa dibayangkan sebagai kabinet dengan dua file bertanda dengan label \"name\" dan \"age\".\n\n![user object](object-user.svg)\n\nKita bisa tambah, hapus dan baca file darinya kapanpun.\n\nNilai properti bisa diakses memakai notasi dot:\n\n```js\n// ambil nilai properti objek:\nalert( user.name ); // John\nalert( user.age ); // 30\n```\n\nNilainya bisa tipe apapun. Ayo tambah nilai boolean:\n\n```js\nuser.isAdmin = true;\n```\n\n![user object 2](object-user-isadmin.svg)\n\nUntuk menghapus properti, kita bisa pakai operator `delete`:\n\n```js\ndelete user.age;\n```\n\n![user object 3](object-user-delete.svg)\n\nKita juga bisa memakai nama properti multi-kata, tapi mereka harus diquotasi:\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30,\n  \"likes birds\": true  // nama properti multi-kata harus diquotasi\n};\n```\n\n![](object-user-props.svg)\n\n\nProperti terakhir di daftar bisa berakhir dengan koma:\n```js\nlet user = {\n  name: \"John\",\n  age: 30*!*,*/!*\n}\n```\nItu disebut koma \"buntut\" atau \"menggantung\". Memudahkan kita menambah/menghapus/memindahkan properti, karena semua barus menjadi mirip.\n\n## Kurung siku\n\nUntuk properti multi-kata, akses dot tak bekerja:\n\n```js run\n// ini akan memberi galat syntax\nuser.likes birds = true\n```\n\nJavascript tidak akan mengerti itu dan akan menganggap kita mencoba mengakses `user.likes`, lalu akan memberikan sintaks error saat sampai ke bagian yang tidak terduga `birds`.\n\nTitik/dot membutukan sebuah kunci/key untuk menjadi identifier yang valid. Berarti: tidak ada spasi, tidak dimulai dengan angka dan tidak mengandung karakter khusus (`$` dan `_` diperbolehkan).\n\nAda alternatif \"notasi bracket kotak\" yang bekerja dengan string apapun:\n\n```js run\nlet user = {};\n\n// set\nuser[\"likes birds\"] = true;\n\n// get\nalert(user[\"likes birds\"]); // true\n\n// delete\ndelete user[\"likes birds\"];\n```\n\nSekarang semuanya oke. Tolong catat bahwa string di dalam bracket diquotasi dengan benar (bisa tipe quotasi apapun).\n\nBracket kotak juga menyediakan cara memperoleh nama properti sebagai hasil expresi -- lawannya string literal -- seperti dari variabel berikut:\n\n```js\nlet key = \"likes birds\";\n\n// sama dengan user[\"likes birds\"] = true;\nuser[key] = true;\n```\n\nDi sini, variabel `key` bisa dikalkulasi saat run-time atau tergantung input user. Lalu kita pakai untuk mengakses properti. Ini memberi kita flexibilitas yang sangat besar.\n\nMisalnya:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\nlet key = prompt(\"What do you want to know about the user?\", \"name\");\n\n// akses dari variabel\nalert( user[key] ); // John (jika mengenter \"name\")\n```\n\nNotasi dot tak bisa dipakai dalam cara serupa:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\nlet key = \"name\";\nalert( user.key ) // undefined\n```\n\n### Properti terkomputasi\n\nKita bisa menggunakan kurung siku didalam objek literal, ketika kita membuat objek. Dipanggil dengan *properti terkomputasi*\n\nMisalnya:\n\n```js run\nlet fruit = prompt(\"Which fruit to buy?\", \"apple\");\n\nlet bag = {\n*!*\n  [fruit]: 5, // nama properti diambil dari variabel fruit\n*/!*\n};\n\nalert( bag.apple ); // 5 jika fruit=\"apple\"\n```\n\nArti properti terkomputasi simpel: `[fruit]` artinya nama properti harus diambil dari `fruit`.\n\nJadi, jika pengunjung mengenter `\"apple\"`, `bag` akan menjadi `{apple: 5}`.\n\nEssensinya, ia bekerja mirip dengan:\n```js run\nlet fruit = prompt(\"Which fruit to buy?\", \"apple\");\nlet bag = {};\n\n// ambil nama properti dari variabel fruit\nbag[fruit] = 5;\n```\n\n...Tapi lebih manis.\n\nKita bisa pakai expresi rumit di dalam bracket kotak:\n\n```js\nlet fruit = 'apple';\nlet bag = {\n  [fruit + 'Computers']: 5 // bag.appleComputers = 5\n};\n```\n\nBracket kotak jauh lebih kuat dari notasi dot. Mereka membolehkan variabel dan nama properti apapun. Tapi mereka juga lebih rumit untuk ditulis.\n\nJadi seringnya, saat nama properti diketahui dan simpel, dot dipakai. Dan jika kita butuh sesuatu yang rumit, maka kita ganti ke bracket kotak.\n\n## Singkatan nilai properti\n\nDi kode riil kita sering memakai variabel sebagai nilai untuk nama properti.\n\nMisalnya:\n\n```js run\nfunction makeUser(name, age) {\n  return {\n    name: name,\n    age: age,\n    // ...properti lainnya\n  };\n}\n\nlet user = makeUser(\"John\", 30);\nalert(user.name); // John\n```\n\nDi contoh di atas, properti punya nama sama dengan variabel. Use-case penggunaan properti dari variabel sangat umum, bahwa ada *singkatan nilai properti* spesial yang memperpendek itu.\n\nKetimbang `name:name` kita bisa menuliskan `name`, seperti ini:\n\n```js\nfunction makeUser(name, age) {\n*!*\n  return {\n    name, // sama dengan name: name\n    age   // sama dengan age: age\n    // ...\n  };\n*/!*\n}\n```\n\nKita bisa pakai baik singkatan dan properti normal bersamaan dalam satu objek:\n\n```js\nlet user = {\n  name,  // sama dengan name:name\n  age: 30\n};\n```\n\n\n## Batasan nama properti\n\nSeperti yang sudah kita tahu, sebuah variabel tidak bisa memiliki nama yang sama dengan salah satu \"kata yang telah dimiliki bahasa pemrograman\" seperti \"for\", \"let\", \"return\" dan lainnya.\n\nTapi dari sebuah properti objek, tidak ada batasan seperti itu:\n\n```js run\n// properti seperti ini bisa digunakan\nlet obj = {\n  for: 1,\n  let: 2,\n  return: 3\n};\n\nalert( obj.for + obj.let + obj.return );  // 6\n```\n\nSingkatnya, tidak ada batasan dalam pemberian nama properti. Bisa digunakan string atau simbol apapun (untuk tipe identifier spesial, akan dibahas nanti).\n\nUntuk tipe lainnya akan secara otomatis diubah kebentuk string.\n\nContoh, angka `0` menjadi string `\"0\"` ketika digunakan sebagai kunci/key properti:\n\n```js run\nlet obj = {\n  0: \"test\" // sama dengan \"0\": \"test\"\n};\n\n// kedua alert mengakses properti yang sama (angka 0 diubah menjadi string \"0\")\nalert( obj[\"0\"] ); // test\nalert( obj[0] ); // test (properti yang sama)\n```\n\nAda hal kecil dengan properti spesial yang bernama `__proto__`. Kita tidak bisa mengatur nilai non-objek kedalamnya:\n\n```js run\nlet obj = {};\nobj.__proto__ = 5; // memasukan angka\nalert(obj.__proto__); // [object Object] - tidak ada nilai didalam objek, tidak bekerja seperti yang diinginkan\n```\n\nSeperti yang kita lihat didalam kode, memasukan nilai primitif `5` akan diabaikan.\n\nKita akan membahas sifat alami dari `__proto__` didalam [bab selanjutnya](info:prototype-inheritance), dan menyarankan [cara untuk membenarkan](info:prototype-methods) sifatnya.\n\n## Test sebuah keberadaan properti, operator \"in\"\n\nFitur yang bisa dicatat didalam objek Javascript, dibandingkan dengan bahasa lainnya, adalah memungkinkannya untuk mengakses properti apapun. Tidak akan terjadi error jika propertinya tidak ada!\n\nMembaca sebuah properti yang tidak ada akan mengembalikan `undefined`. Jadi kita akan dengan mudah mengetest apakah propertinya ada atau tidak:\n\n```js run\nlet user = {};\n\nalert( user.noSuchProperty === undefined ); // true artinya \"tak ada properti macam ini\"\n```\n\nAda juga operator spesial `\"in\"` untuk mengecek existensi properti.\n\nSyntaxnya:\n```js\n\"key\" in object\n```\n\nMisalnya:\n\n```js run\nlet user = { name: \"John\", age: 30 };\n\nalert( \"age\" in user ); // true, user.age ada\nalert( \"blabla\" in user ); // false, user.blabla tak ada\n```\n\nTolong ingat bahwa di sebelah kiri `in` harus ada *nama properti*. Itu biasanya string yang dikuotasi.\n\nJika kita menghilangkan kutipnya, berarti sebuah variabel, itu haruslah mengandung nama yang akan dites. Contoh:\n\n```js run\nlet user = { age: 30 };\n\nlet key = \"age\";\nalert( *!*key*/!* in user ); // true, properti \"age\" ada\n```\n\nLalu kenapa ada operator `in`? Bukankah cukup untuk membandingkannya dengan `undefined`?\n\nBaik, kebanyakan waktu perbandingan dengan `undefined` bekerja dengan semestinya. Akan tetapi ada kasus spesial dimana itu akan gagal, tapi dengan `\"in\"` akan berjalan dengan baik.\n\n\nItu ialah saat ada properti objek, tapi menyimpan `undefined`:\n\n```js run\nlet obj = {\n  test: undefined\n};\n\nalert( obj.test ); // mengembalikan undefined, apakah propertinya tidak ada?\n\nalert( \"test\" in obj ); // true, propertinya ada!\n```\n\n\nDi contoh kode di atas, properti `obj.test` ada secara teknis. Tapi operator `in` bekerja dengan baik.\n\nSituasi seperti ini jarang terjadi, karena `undefined` biasanya tak ditetapkan. Kita sering memakai `null` untuk nilai \"unknown\" atau \"empty\". Jadi operator `in` merupakan tamu exotik dalam kode.\n````\n\n## \"for..in\"\n\nUntuk mengitari semua kunci objek, ada bentuk spesial dari loop: `for..in`. Ini sangat berbeda dari konstruksi `for(;;)` yang kita pelajari sebelumnya.\n\nSyntaxnya:\n\n```js\nfor (key in object) {\n  // mengexekusi badan untuk tiap kunci dalam properti objek\n}\n```\n\nMisalnya, mari mengoutkan semua properti `user`:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n  isAdmin: true\n};\n\nfor (let key in user) {\n  // keys\n  alert( key );  // name, age, isAdmin\n  // nilai untuk key\n  alert( user[key] ); // John, 30, true\n}\n```\n\nCatat bahwa semua konstruksi \"for\" membolehkan kita mendeklarasi variabel looping di dalam loop, seperti `let key` di sini.\n\nJuga, kita bisa memakai nama variabel lain di sini ketimbang `key`. Misalnya, `\"for (let prop in obj)\"` juga banyak dipakai.\n\n\n### Berurut seperti objek\n\nApa objek terurut? Dengan kata lain, jika kita meloop satu objek keseluruhan, apa kita mengambil semua properti dengan urutan yang sama saat mereka ditambahkan? Apa kita bisa percaya itu?\n\nJawaban pendeknya ialah: \"terurut dalam cara spesial\": properti integer terurut, yang lainnya muncul dalam urutan pembuatan. Detilnya mengikuti.\n\nMisalnya, mari pertimbangkan objek dengan kode telpon:\n\n```js run\nlet codes = {\n  \"49\": \"Germany\",\n  \"41\": \"Switzerland\",\n  \"44\": \"Great Britain\",\n  // ..,\n  \"1\": \"USA\"\n};\n\n*!*\nfor (let code in codes) {\n  alert(code); // 1, 41, 44, 49\n}\n*/!*\n```\n\nObjek ini digunakan untuk mensugesti daftar opsi ke pengguna. Jika kita membuat situs khusus untuk audiensi Jerman maka kita kemungkinan mau `49` jadi yang pertama.\n\nTapi jika kita menjalankan kodenya, kita lihat potret yang berbeda:\n\n- USA (1) goes first\n- then Switzerland (41) and so on.\n\nKode telpon berurut secara ascending, karena mereka integer. Jadi kita lihat `1, 41, 44, 49`.\n\n````smart header=\"Properti integer? Apa itu?\"\nIstilah \"properti integer\" di sini artinya string yang bisa dikonversi ke-dan-dari integer tanpa perubahan.\n\nJadi, \"49\" nama properti integer, karena mereka ditransform ke angka integer dan kebalikannya, ia masih sama saja. Tapi \"+49\" dan \"1.2\" tidak:\n\n```js run\n// Math.trunc is a built-in function that removes the decimal part\nalert( String(Math.trunc(Number(\"49\"))) ); // \"49\", sama, properti integer\nalert( String(Math.trunc(Number(\"+49\"))) ); // \"49\", tidak sama \"+49\" ⇒ bukan properti integer\nalert( String(Math.trunc(Number(\"1.2\"))) ); // \"1\", tidak sama \"1.2\" ⇒ bukan properti integer\n```\n````\n\n...Di sisi lain, jika kuncinya non-integer, maka mereka didaftar dalam urutan kreasi, misalnya:\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\nuser.age = 25; // tambah satu lagi\n\n*!*\n// properti non-integer didaftar dalam order kreasi\n*/!*\nfor (let prop in user) {\n  alert( prop ); // name, surname, age\n}\n```\n\nJadi, untuk mengatasi isu kode telpon, kita bisa berbuat \"curang\" dengan menjadikan kode non-integer. Cukup menambahkan tanda plus `\"+\"` sebelum tiap kode.\n\nSeperti ini:\n\n```js run\nlet codes = {\n  \"+49\": \"Germany\",\n  \"+41\": \"Switzerland\",\n  \"+44\": \"Great Britain\",\n  // ..,\n  \"+1\": \"USA\"\n};\n\nfor (let code in codes) {\n  alert( +code ); // 49, 41, 44, 1\n}\n```\n\nSekarang itu bekerja sesuai yang diinginkan.\n\n## Ringkasan\n\nObjek adalah array asosiatif dengan beberapa fitur spesial.\n\nObjek menyimpan properti (pasangan key-value), dimana:\n- kunci/key properti haruslah sebuah string atau simbol (biasanya string).\n- Nilai bisa tipe apapun.\n\nUntuk mengakses properti, kita bisa gunakan:\n- Notasi dot: `obj.properti`.\n- Notasi kurung siku `obj[\"properti\"]`. Kurung siku memperbolehkan mengambil key dari sebuah variabel, seperti `obj[varDenganKey]`.\n\nOperator tambahan:\n- Untuk menghapus properti: `delete obj.prop`.\n- Untuk memeriksa jika properti dengan nilai yang diberikan ada: `\"key\" in obj`.\n- Untuk mengiterasi sebuah objek: `for (let key in obj)` loop.\n\nDi bab ini kita sudah belajar apa yang dipanggil dengan \"plain object\" atau \"Objek sederhana\" atau `Object`.\n\nMasuk ada banyak hal tentang objek didalam Javascript:\n\n- `Array` untuk menyimpan koleksi data,\n- `Date` untuk menyimpan informasi tentang tanggal dan waktu,\n- `Error` untuk menyimpan informasi tentang sebuah error.\n- ...Dan lainnya.\n\nMereka mempunyai fitur spesial lainnya yang akan kita pelajari nanti. Terkadang orang-orang berkata seperti \"Tipe array\" atau \"tipe tanggal/waktu\", akan tetapi secara formal mereka bukanlah tipe yang mereka miliki sendiri, tapi milik sebuah tipe data \"objek\" tunggal. dan mereka bisa meluas ke berbagai arah.\n\nObjek didalam Javascript sangatlah kuat. Kita baru saja belajar sedikit saja tentang topiknya yang sebenarnya sangat luas. Kita akan belajar lebih tentang objek dan belajar tentang objek di bagian selanjutnya."
  },
  {
    "path": "1-js/04-object-basics/02-object-copy/article.md",
    "content": "# Referensi objek dan menyalinnya\n\nSalah satu perbedaan mendasar dari objek versus primitif adalah bahwa objek disimpan dan disalin \"dengan referensi\", sedangkan nilai primitif: string, angka, boolean, dll - selalu disalin \"sebagai nilai keseluruhan\".\n\nItu mudah dipahami jika kita melihat sedikit ke belakang tentang apa yang terjadi saat kita menyalin sebuah nilai.\n\nMari kita mulai dengan yang primitif, seperti string.\n\nDi sini kami memasukkan salinan `pesan` ke dalam` frase`:\n\n```js\nlet message = \"Hello!\";\nlet phrase = message;\n```\n\nSebagai hasilnya kita punya dua variabel yang berdiri sendiri, dan keduanya menyimpan nilai string `\"Hello!\"`.\n\n![](variable-copy-value.svg)\n\nHasil yang cukup jelas, bukan?\n\nObjek tidak seperti itu.\n\n**Sebuah variabel tidak menyimpan objek itu sendiri, akan tetapi \"disimpan didalam memori\", dengan kata lain \"mereferensi\" kepadanya (ke data didalam memori).**\n\nMari kita lihat contoh variabel tersebut:\n\n```js\nlet user = {\n  name: \"John\"\n};\n```\n\nDan ini bagaimana kita menyimpannya di dalam memory:\n\n![](variable-contains-reference.svg)\n\nObjek disimpan di suatu tempat di memori (di sebelah kanan gambar), sedangkan variabel `user` (di sebelah kiri) memiliki\" referensi \"padanya.\n\nKita mungkin menganggap variabel objek, seperti `pengguna`, seperti selembar kertas dengan alamat objek di atasnya.\n\nSaat kita melakukan tindakan dengan objek, misalnya: mengambil properti `user.name`, mesin JavaScript melihat apa yang ada di alamat itu dan melakukan operasi pada objek sebenarnya.\n\nSekarang inilah mengapa itu penting.\n\n**Ketika sebuah variabel objek disalin -- referensinya akan tersalin, objeknya tidak terduplikasi.**\n\nContoh:\n\n```js no-beautify\nlet user = { name: \"John\" };\n\nlet admin = user; // menyalin referensinya\n```\n\nKita sekarang punya dua variabel, masing-masing mereferensi ke objek yang sama:\n\n![](variable-copy-reference.svg)\n\nSeperti yang Anda lihat, masih ada satu objek, sekarang dengan dua variabel yang mereferensikannya.\n\nKita dapat menggunakan variabel apa saja untuk mengakses objek dan mengubah isinya:\n\n\n```js run\nlet user = { name: 'John' };\n\nlet admin = user;\n\n*!*\nadmin.name = 'Pete'; // mengganti admin dengan menggunakan \"referensi\"\n*/!*\n\nalert(*!*user.name*/!*); // 'Pete', perubahan akan terlihat pada \"user\"\n```\n\nSeolah-olah kita memiliki lemari dengan dua kunci dan menggunakan salah satunya (admin) untuk masuk ke dalamnya dan membuat perubahan. Kemudian, jika nanti kita menggunakan kunci lain (pengguna), kita masih membuka lemari yang sama dan dapat mengakses konten yang diubah.\n\n## Perbandingan dengan referensi\n\n**Dua objek adalah sama jika mereka objek yang sama.**\n\nDibawah adalah dua variabel yang mereferensi ke objek yang sama, dengan demikian mereka sama:\n\nContohnya, di sini a dan b mereferensikan objek yang sama, sehingga keduanya sama:\n\n```js run\nlet a = {};\nlet b = a; // menyalin referensi\n\nalert( a == b ); // true, kedua variabel mereferensi ke objek yang sama\nalert( a === b ); // true\n```\n\nDan dibawah adalah dua objek yang berdiri sendiri, tidaklah sama, walaupun keduanya kosong:\n\n```js run\nlet a = {};\nlet b = {}; // dua objek yang berdiri sendiri\n\nalert( a == b ); // false\n```\n\nUntuk perbandingan seperti `obj1 > obj2` atau untuk perbandingan dengan sebuah nilai primitif `obj == 5`, objek akan diubah dahulu menjadi primitif. Kita akan belajar bagaimana perubahan objek sebentar lagi, akan tetapi sebenarnya, perbandingan seperti itu muncul sangat jarang, biasanya hanya sebuah hasil dari kesalahan koding.\n\n## Penggandaan dan penggabungan, Object.assign [#cloning-and-merging-object-assign]\n\nJadi, menyalin sebuah variabel objek akan menciptakan satu lagi referensi kepada objek yang sama.\n\nTapi bagaimana jika kita butuh untuk menduplikasi objek? Membuat salinan yang berdiri sendiri, menggandakan atau meng-klon?\n\nHal itu juga bisa dilakukan, tapi sedikit lebih sulit, karena tidak ada method bawaan untuk hal itu di javascript. Sebenarnya, hal itu juga jarang dibutuhkan. Di kebanyakan waktu, menyalin referensinya sudah cukup.\n\nTapi bagaimana jika kita benar-benar ingin hal itu, lalu kita membutuhkan untuk menciptakan sebuah objek dan mengulangi struktur dari objek yang sama dengan meng-iterasi propertinya dan menyalin mereka didalam level primitif.\n\nSeperti ini:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet clone = {}; // objek kosong baru\n\n// salin semua properti user kedalamnya\nfor (let key in user) {\n  clone[key] = user[key];\n}\n*/!*\n\n// sekarang clone adalah sebuah objek yang berdiri sendiri dengan konten yang sama\nclone.name = \"Pete\"; // ubah data didalamnya\n\nalert( user.name ); // masih John didalam objek yang asli\n```\n\nJuga kita bisa menggunakan method [Object.assign](mdn:js/Object/assign) untuk itu.\n\nsintaksnya adalah:\n\n```js\nObject.assign(dest, [src1, src2, src3...])\n```\n\n- Argumen pertama `dest` adalah sebuah objek target.\n- Argumen selanjutnya `src1, ..., srcN` (bisa sebanyak yang dibutuhkan) adalah objek sumber.\n- Itu akan menyalin properti dari seluruh objek sumber `src1, ..., srcN` kedalam target `dest`. Dengan kata lain, properti dari semua argumen dimulai dari argumen kedua akan disalin kedalam object pertama.\n- Setelah pemanggilan akan mengembalikan `dest`.\n\n\nContoh, kita bisa menggunakan untuk menggabungkan beberapa objek menjadi satu:\n```js\nlet user = { name: \"John\" };\n\nlet permissions1 = { canView: true };\nlet permissions2 = { canEdit: true };\n\n*!*\n// menyalin seluruh properti dari permission1 dan permission2 kedalam user\nObject.assign(user, permissions1, permissions2);\n*/!*\n\n// sekarang user = { name: \"John\", canView: true, canEdit: true }\n```\n\nJika nama dari properti yang disali sudah ada, propertinya akan ditimpa:\n\n```js run\nlet user = { name: \"John\" };\n\nObject.assign(user, { name: \"Pete\" });\n\nalert(user.name); // sekarang user = { name: \"Pete\" }\n```\n\nKita juga bisa menggunakan `Object.assign` untuk mengganti perulangan `for...in` untuk penggandaan yang sederhana.\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet clone = Object.assign({}, user);\n*/!*\n```\n\nKode diatas akan menyalin seluruh properti dari `user` kedalam objek yang kosong dan mengembalikan/me-return hasilnya.\n\nAda juga metode lain untuk mengkloning objek, mis. menggunakan [sintaksis spread](info:rest-parameters-spread) `clone = {...user}`, dibahas nanti dalam tutorial.\n\n## Nested cloning\n\nSampai sekarang kita telah berasumsi bahwa seluruh properti dari `user` adalah primitif. Tapi properti bisa di referensi ke objek lainnya. Apa yang harus dilakukan dengan mereka?\n\nLike this:\n```js run\nlet user = {\n  name: \"John\",\n  sizes: {\n    height: 182,\n    width: 50\n  }\n};\n\nalert( user.sizes.height ); // 182\n```\n\nNow it's not enough to copy `clone.sizes = user.sizes`, because the `user.sizes` is an object, it will be copied by reference. So `clone` and `user` will share the same sizes:\nSekarang hal itu tidak cukup untuk menyalin `clone.sizes = user.sizes`, karena `user.sizes` adalah sebuah objek, itu akan tersalin secara referensi. Jadi `clone` dan `user` akan berbagi objek yang sama:\n\nLike this:\n\n```js run\nlet user = {\n  name: \"John\",\n  sizes: {\n    height: 182,\n    width: 50\n  }\n};\n\nlet clone = Object.assign({}, user);\n\nalert( user.sizes === clone.sizes ); // true, objek yang sama\n\n// user dan clone akan berbagi objek yang sama\nuser.sizes.width++;       // ganti properti dari satu tempat\nalert(clone.sizes.width); // 51, melihat hasilnya ditempat yang lain\n```\n\nUntuk membenarkan hal itu, kita harus menggunakan perulangan kloning yang memeriksa setip nilai dari `user[key]` dan, jika itu adalah sebuah objek, lalu duplikasi strukturnya juga. Hal itu dinamakan dengan \"deep cloning\".\n\nWe can use recursion to implement it. Or, to not reinvent the wheel, take an existing implementation, for instance [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep) from the JavaScript library [lodash](https://lodash.com).\n\n\n````smart header=\"Const objects can be modified\"\nAn important side effect of storing objects as references is that an object declared as `const` *can* be modified.\n\nFor instance:\n\n```js run\nconst user = {\n  name: \"John\"\n};\n\n*!*\nuser.name = \"Pete\"; // (*)\n*/!*\n\nalert(user.name); // Pete\n```\n\nSepertinya baris `(*)` akan menyebabkan kesalahan, tetapi sebenarnya tidak. Nilai dari `user` adalah konstan, itu harus selalu mereferensikan objek yang sama, tetapi properti dari objek tersebut bebas untuk berubah.\n\nDengan kata lain, `const user` memberikan kesalahan hanya jika kita mencoba menyetel` user = ... `secara keseluruhan.\n\nyang berarti, jika kita benar-benar perlu membuat properti objek konstan, itu juga mungkin, tetapi menggunakan metode yang sama sekali berbeda. Kita akan membahasnya di bab <info: property-descriptors>.\n````\n\n\n## Ringkasan\n\nobjek dibuat dan disalin dengan menggunakan referensi. Dengan kata lain, sebuah variable menyimpan bukanlah \"nilai objek\", tapi sebuah \"referensi\" (address/alamat di memori) untuk nilainya. Jadi menyalin sebuah variabel atau memindahkannya sebagai fungsi argumen akan menyalin referensinya, bukan objeknya.\n\nSemua operasi yang disalin dengan menggunakan referensi (seperti menambah/menghapus properti) dilakukan didalam satu objek yang sama.\n\nUntuk membuat \"salinan asli\" (kloning) kita dapat menggunakan `Object.assign` untuk apa yang disebut \"shallow copy\"(objek bersarang disalin dengan referensi) atau fungsi\"deep cloning\", seperti [_.cloneDeep (obj)](https://lodash.com/docs#cloneDeep).        \n\n\n\n\n"
  },
  {
    "path": "1-js/04-object-basics/03-garbage-collection/article.md",
    "content": "# Pengumpulan sampah (_Garbage collection_)\n\nManajemen memori di JavaScript dilakukan secara otomatis dan tak terlihat oleh kita. Kita membuat _primitive_, objek, fungsi... Semua yang membutuhkan memori.\n\nApa yang terjadi ketika sesuatu yang telah kita buat tersebut sudah tidak diperlukan? Bagaimana _engine_ JavaScript menemukan dan membersihkannya?\n\n## Keterjangkauan (_Reachability_)\n\nKonsep utama manajemen memori di JavaScript ialah *keterjangkauan*.\n\nSederhananya, sebuah nilai yang \"terjangkau\" adalah mereka yang masih dapat diakses atau dapat digunakan. Mereka dapat dipastikan tersimpan di memori.\n\n1. Ada sekumpulan nilai-nilai yang terjangkau secara inheren, yang tak dapat dihapus untuk alasan yang jelas.\n\n    Contohnya:\n\n    - Variabel lokal dan parameter-parameter dari fungsi (yang di eksekusi) saat ini.\n    - Variabel-variabel dan parameter-parameter dari fungsi-fungsi lain yang terkait dengan rantai panggilan bersarang saat ini.\n    - Variabel-variabel global.\n    - (ada beberapa hal lain, yang internal juga)\n\n    Nilai-nilai tadi disebut *roots*.\n\n2. Nilai lainnya dianggap terjangkau jika dapat dijangkau dari sebuah _root_ melalui sebuah rujukkan atau rantai rujukkan.\n\n    Contoh, jika terdapat sebuah objek didalam global variabel, dan objek tersebut memiliki sebuah properti yang mereferensi objek lain, objek itu dianggap dapat dijangkau. Dan referensinya juga bisa dijangkau. Contoh lengkap dibawah ini.\n\nAda sebuah _background process_ di _engine_ JavaScript yang disebut [garbage collector](https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)). Ia mengamati seluruh objek dan menyingkirkan semua yang sudah tak terjangkau.\n\n## Contoh Sederhana\n\nBerikut adalah contoh paling sederhana:\n\n```js\n// user memiliki rujukkan terhadap objek\nlet user = {\n  name: \"John\"\n};\n```\n\n![](memory-user-john.svg)\n\nTanda panah disini menggambarkan sebuah rujukkan objek. Variabel global `\"user\"` merujuk objek `{name: \"John\"}` (kita sebut John supaya singkat). _Property_ `\"name\"` dari objek John menyimpan sebuah _primitive_, jadi itu disematkan di dalam objek.\n\nJika nilai dari `user` ditimpa, maka rujukkannya hilang:\n\n```js\nuser = null;\n```\n\n![](memory-user-john-lost.svg)\n\nSekarang John menjadi tak terjangkau. Tak ada cara untuk mengaksesnya, tak ada rujukkan terhadapnya. _Garbage collector_ akan membuang data tersebut and membebaskan memori.\n\n## Dua rujukkan\n\nSekarang bayangkan kita menyalin rujukkan dari `user` ke `admin`:\n\n```js\n// user memiliki rujukkan terhadap objek\nlet user = {\n  name: \"John\"\n};\n\n*!*\nlet admin = user;\n*/!*\n```\n\n![](memory-user-john-admin.svg)\n\nSekarang jika kita melakukan hal yang sama:\n```js\nuser = null;\n```\n\n...Maka objek \"John\" tersebut masih bisa dijangkau lewat variabel global `admin`, jadi masih ada di memori. Jika kita menimpa `admin` juga, barulah dapat dihilangkan.\n\n## Objek-objek yang saling terkait\n\nSekarang contoh yang lebih kompleks. Keluarga:\n\n```js\nfunction marry(man, woman) {\n  woman.husband = man;\n  man.wife = woman;\n\n  return {\n    father: man,\n    mother: woman\n  }\n}\n\nlet family = marry({\n  name: \"John\"\n}, {\n  name: \"Ann\"\n});\n```\n\nFungsi `marry` \"mengawinkan\" dua objek dengan memberikan keduanya rujukkan satu sama lain dan mengembalikan sebuah objek baru yang berisikan kedua objek tersebut.\n\nHasil struktur memorinya ialah :\n\n![](family.svg)\n\nDisini, semua objek terjangkau.\n\nSekarang mari hapus dua rujukkan:\n\n```js\ndelete family.father;\ndelete family.mother.husband;\n```\n\n![](family-delete-refs.svg)\n\nTak cukup hanya dengan menghapus salah satu dari dua rujukkan tersebut, karena semua objek masih bisa dijangkau.\n\nTetapi jika kita menghapus keduanya, maka dapat kita lihat bahwa John tak lagi memiliki objek yang merujukkannya:\n\n![](family-no-father.svg)\n\nRujukkan keluar (_outgoing reference_) tidak masalah. Hanya rujukkan masuk (_incoming reference_) yang dapat membuat sebuah objek terjangkau. Jadi, sekarang John tak terjangkau dan akan dihapus dari memori bersama semua datanya yang juga tak dapat diakses.\n\nSetelah _garbage collection_:\n\n![](family-no-father-2.svg)\n\n## Pulau tak terjangkau\n\nMungkin saja satu kumpulan (pulau) objek yang saling tertaut menjadi tak terjangkau dan dihapus dari memori.\n\nObjeknya sama seperti diatas. Kemudian:\n\n```js\nfamily = null;\n```\n\nGambaran _in-memory_-nya menjadi:\n\n![](family-no-family.svg)\n\nContoh ini menunjukkan bagaimana pentingnya konsep keterjangkauan (_reachability_).\n\nSudah jelas bahwa John dan Ann masih tertaut, keduanya memiliki rujukkan masuk. Tapi itu saja tak cukup.\n\nObjek `\"family\"` diatas telah menjadi tak terhubung dengan _root_, tak ada lagi rujukkan terhadapnya, sehingga keseluruhan pulau kumpulan objek tersebut menjadi tak terjangkau dan akan dihapus.\n\n## Algoritma internal\n\nAlgoritma _garbage collection_ dasar disebut _\"mark-and-sweep\"_.\n\nLangkah _\"garbage collection\"_ berikut dilakukan secara teratur:\n\n- _Garbage collector_ mengambil objek _roots_ dan \"menandai\" (_marks_ / mengingat) mereka.\n- Kemudian ia mendatangi dan \"menandai\" semua rujukkannya.\n- Kemudian ia mendatangi objek yang telah ditandai tersebut dan menandai rujukkan *mereka*. Semua objek yang telah dikunjungi akan diingat, agar nantinya tidak mengunjungi objek yang sama dua kali.\n- ...Dan seterusnya sampai semua rujukkan yang dapat dijangkau (dari _roots_) telah dikunjungi.\n- Semua objek kecuali yang ditandai akan dihapus.\n\nContohnya, semisal kita memiliki struktur objek seperti berikut:\n\n![](garbage-collection-1.svg)\n\nDapat kita lihat dengan jelas \"pulau tak terjangkau\" di sisi kanan. Sekarang mari kita lihat bagaimana _\"mark-and-sweep\" garbage collector_ berurusan dengannya.\n\nLangkah pertama menandai _roots_-nya:\n\n![](garbage-collection-2.svg)\n\nKemudian rujukkannya ditandai:\n\n![](garbage-collection-3.svg)\n\n...Dan kemudian rujukkan dalamnya juga, jika masih ada:\n\n![](garbage-collection-4.svg)\n\nSekarang objek-objek yang tak dapat dikunjungi selama proses berlangsung dianggap tak terjangkau (_unreachable_) dan akan dihapus:\n\n![](garbage-collection-5.svg)\n\nKita juga bisa membayangkan proses tersebut sebagai menumpahkan ember cat dari _roots_, yang mengalir ke semua rujukkan dan menandai semua objek yang terjangkau. Yang tidak tertandai akan dihapus.\n\nItu merupakan konsep dari bagaimana cara kerja _garbage collection_. _Engines_ JavaScript menerapkan banyak optimisasi untuk membuatnya berjalan lebih cepat dan tanpa mempengaruhi eksekusi.\n\nBeberapa optimisasi:\n\n- **Generational collection** -- objek-objek dibagi kedalam dua set: \"yang baru\" dan \"yang lama\". Kebanyakan objek muncul, melakukan tugasnya dan mati dengan cepat, mereka dapat dibersihkan secara agresif. Mereka yang bertahan cukup lama, akan menjadi \"yang lama\" dan tak akan sering diperiksa.\n- **Incremental collection** -- Jika terdapat banyak objek-objek, dan kita mencoba menapaki sambil menandai keseluruhan set objek sekaligus, itu dapat memakan waktu dan menimbulkan keterlambatan yang terlihat dalam eksekusi. Jadi _engine_ akan mencoba untuk memecah proses _garbage collection_ menjadi bagian-bagian kecil. Kemudian bagian-bagian kecil tersebut akan dieksekusi satu-persatu, secara terpisah. Itu memerlukan pencatatan ekstra diantara mereka untuk melacak perubahan, tetapi jadinya kta hanya mengalami keterlambatan kecil yang banyak daripada satuan yang besar.\n- **Idle-time collection** -- _garbage collector_ akan mencoba untuk jalan hanya ketika _CPU_ sedang _idle_, untuk mengurangi kemungkinan efek pada eksekusi.\n\nTerdapat optimisasi-optimisasi dan tipe-tipe lain dari algoritma _garbage collection_. Sebesar apapun keinginan untuk menjelaskannya disini, harus kutahan, karena _engines_ yang berbeda mengimplementasikan teknik dan _tweaks_ yang berbeda pula. Dan, yang lebih penting, hal-hal tersebut akan berubah seiring dengan pengembangan _engine_, jadi mempelajarinya lebih dalam \"di awal\", tanpa kebutuhan yang berarti mungkin akan sia-sia. Kecuali, tentu saja, jika itu merupakan murni masalah ketertarikan, maka ada beberapa tautan untukmu dibawah.\n\n## Ringkasan\n\nHal utama yang perlu diketahui:\n\n- Pengumpulan sampah (_Garbage collection_) dilakukan secara otomatis. Kita tidak bisa memaksa ataupun mencegahnya.\n- Objek-objek dipertahankan dalam memori selagi mereka terjangkau (_reachable_).\n- Menjadi yang dirujuk tidak sama dengan menjadi terjangkau (dari sebuah _root_): sekumpulan objek yang saling terkait dapat menjadi tak terjangkau sebagai keseluruhan.\n\n_Engine_ modern mengimplementasikan algoritma  _garbage collection_ canggih (_advance_).\n\nBuku \"The Garbage Collection Handbook: The Art of Automatic Memory Management\" (R. Jones et al) mencakup beberapanya.\n\nJika kamu familiar dengan pemrograman _low-level_, informasi mendalam tentang _garbage collector_ V8 terdapat pada artikel [A tour of V8: Garbage Collection](http://jayconrod.com/posts/55/a-tour-of-v8-garbage-collection).\n\n[V8 blog](https://v8.dev/) juga mempublikasikan artikel-artikel tentang ubahan-ubahan dalam manajemen memori dari waktu ke waktu. Tentu saja, untuk belajar proses _garbage collection_, kamu lebih baik mempersiapkan diri dengan belajar tentang _internals_ V8 secara umum dan membaca blog [Vyacheslav Egorov](http://mrale.ph) yang merupakan salah seorang _engineer_ V8. Saya bilang: \"V8\", karena merupakan yang paling komprehensif di _cover_ oleh artikel-artikel di internet. Untuk _engine_ lainnya, pendekatannya kebanyakan mirip-mirip, tetapi _garbage collection_ berbeda dalam banyak aspek.\n\nPengetahuan mendalam mengenai _engines_ itu bagus ketika membutuhkan optimisasi _low-level_. Tapi akan lebih bijak untuk merencanakan itu sebagai langkah selanjutnya setelah kamu akrab dengan bahasanya (JavaScript).  \n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md",
    "content": "**Jawaban: error.**\n\nCoba ini:\n```js run\nfunction makeUser() {\n  return {\n    name: \"John\",\n    ref: this\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref.name ); // Error: Tidak bisa membaca properti 'name' dari undefined\n```\n\nHal itu karena aturan-aturan yang mengatur `this` tidak melihat definisi objek. Yang penting hanya momen saat panggilan terjadi.\n\nDi sini nilai dari `this` dalam `makeUser()` adalah `undefined`, karena dipanggil sebagai sebuah fungsi, tidak sebagai sebuah metode dengan sintaks \"tanda titik\".\n\nNilai `this` adalah satu untuk keseluruhan fungsi, blok kode serta penulisan objek tidak mempengaruhi nilai tersebut.\n\nJadi `ref: this` sebenarnya mengambil `this` yang sekarang dari fungsi tersebut.\n\nBerikut ini contoh kasus kebalikannya:\n\n```js run\nfunction makeUser() {\n  return {\n    name: \"John\",\n*!*\n    ref() {\n      return this;\n    }\n*/!*\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref().name ); // John\n```\n\nKini kode itu berfugsi, karena `user.ref()` adalah sebuah metode. Dan nilai dari `this` ditentukan ke objek sebelum tanda titik `.`.\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/4-object-property-this/task.md",
    "content": "importance: 5\n\n---\n\n# Menggunakan \"this\" dalam penulisan objek\n\nBerikut ini adalah fungsi `makeUser` yang mengembalikan sebuah objek.\n\nApa hasil dari mengakses `ref`? Mengapa demikian?\n\n```js\nfunction makeUser() {\n  return {\n    name: \"John\",\n    ref: this\n  };\n}\n\nlet user = makeUser();\n\nalert( user.ref.name ); // Apa hasilnya?\n```\n\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/_js.view/solution.js",
    "content": "let calculator = {\n  sum() {\n    return this.a + this.b;\n  },\n\n  mul() {\n    return this.a * this.b;\n  },\n\n  read() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  }\n};"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/_js.view/test.js",
    "content": "\n\ndescribe(\"calculator\", function() {\n  \n  context(\"when 2 and 3 entered\", function() {\n    beforeEach(function() {\n      sinon.stub(window, \"prompt\");\n\n      prompt.onCall(0).returns(\"2\");\n      prompt.onCall(1).returns(\"3\");\n\n      calculator.read();\n    });\n\n    afterEach(function() {\n      prompt.restore();\n    });\n    \n    it('the read get two values and saves them as object properties', function () {\n      assert.equal(calculator.a, 2);\n      assert.equal(calculator.b, 3);\n    });\n\n    it(\"the sum is 5\", function() {\n      assert.equal(calculator.sum(), 5);\n    });\n\n    it(\"the multiplication product is 6\", function() {\n      assert.equal(calculator.mul(), 6);\n    });\n  });\n\n});\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/solution.md",
    "content": "\n```js run demo solution\nlet calculator = {\n  sum() {\n    return this.a + this.b;\n  },\n\n  mul() {\n    return this.a * this.b;\n  },\n\n  read() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  }\n};\n\ncalculator.read();\nalert( calculator.sum() );\nalert( calculator.mul() );\n```\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/7-calculator/task.md",
    "content": "importance: 5\n\n---\n\n# Membuat sebuah kalkulator\n\nBuatlah sebuah objek `calculator` dengan tiga metode:\n\n- `read()` mendorong kedua nilai dan menyimpan nilai-nilai tersebut sebagai properti objek.\n- `sum()` mengembalikan jumlah dari nilai-nilai yang disimpan.\n- `mul()` mengalikan nilai-nilai yang disimpan dan mengembalikan hasilnya.\n\n```js\nlet calculator = {\n  // ... your code ...\n};\n\ncalculator.read();\nalert( calculator.sum() );\nalert( calculator.mul() );\n```\n\n[demo]\n\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/solution.js",
    "content": "\nlet ladder = {\n  step: 0,\n  up: function() { \n    this.step++;\n    return this;\n  },\n  down: function() { \n    this.step--;\n    return this;\n  },\n  showStep: function() { \n    alert(this.step);\n  }\n};"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/_js.view/test.js",
    "content": "\ndescribe('Ladder', function() {\n  before(function() {\n    window.alert = sinon.stub(window, \"alert\");\n  });\n  \n  beforeEach(function() {\n    ladder.step = 0;\n  });\n\n  it('up() should return this', function() {\n    assert.equal(ladder.up(), ladder);\n  });\n\n  it('down() should return this', function() {\n    assert.equal(ladder.down(), ladder);\n  });\n\n  it('showStep() should call alert', function() {\n    ladder.showStep();\n    assert(alert.called);\n  });\n\n  it('up() should increase step', function() {\n    assert.equal(ladder.up().up().step, 2);\n  });\n\n  it('down() should decrease step', function() {\n    assert.equal(ladder.down().step, -1);\n  });\n\n  it('down().up().up().up() ', function() {\n    assert.equal(ladder.down().up().up().up().step, 2);\n  });\n  \n  after(function() {\n    ladder.step = 0;\n    alert.restore();\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md",
    "content": "Solusinya adalah untuk mengembalikan objek itu sendiri dari setiap panggilan.\n\n```js run demo\nlet ladder = {\n  step: 0,\n  up() {\n    this.step++;\n*!*\n    return this;\n*/!*\n  },\n  down() {\n    this.step--;\n*!*\n    return this;\n*/!*\n  },\n  showStep() {\n    alert( this.step );\n*!*\n    return this;\n*/!*\n  }\n};\n\nladder.up().up().down().up().down().showStep(); // 1\n```\n\nKita juga bisa menuliskan sebuah panggilan di setiap baris. Untuk rantai kode yang panjang jadi lebih mudah dibaca seperti ini:\n\n```js\nladder\n  .up()\n  .up()\n  .down()\n  .up()\n  .down()\n  .showStep(); // 1\n```\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/8-chain-calls/task.md",
    "content": "importance: 2\n\n---\n\n# *Chaining* (merantaikan)\n\nAda sebuah objek layaknya tangga (`ladder`) yang dapat naik dan turun:\n\n```js\nlet ladder = {\n  step: 0,\n  up() { \n    this.step++;\n  },\n  down() { \n    this.step--;\n  },\n  showStep: function() { // menunjukkan langkah yang sekarang\n    alert( this.step );\n  }\n};\n```\n\nKini, jika kita perlu untuk membuat beberapa panggilan secara berurutan, bisa dilakukan dengan cara seperti ini:\n\n```js\nladder.up();\nladder.up();\nladder.down();\nladder.showStep(); // 1\n```\n\nModifikasi kode `up`, `down` dan `showStep` untuk membuat panggilan-panggilan tersebut dapat dirantaikan satu sama lain, seperti ini:\n\n```js\nladder.up().up().down().showStep(); // 1\n```\n\nPendekatan demikian digunakan secara luas di banyak *library* JavaScript.\n"
  },
  {
    "path": "1-js/04-object-basics/04-object-methods/article.md",
    "content": "# Metode objek, \"this\"\n\nObjek-objek biasanya dibuat untuk merepresentasikan benda di dunia nyata, seperti para pengguna, perintah dan sebagainya:\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30\n};\n```\n\nDan, di dunia nyata, seorang pengguna bisa *bertindak*: memilih sesuatu dari keranjang belanja, *login*, *logout* dan lain-lain.\n\nTindakan-tindakan direpresentasikan dalam JavaScript dengan fungsi-fungsi dalam properti.\n\n## Contoh metode\n\nSebagai awalan, mari ajarkan `user` untuk bilang hello:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nuser.sayHi = function() {\n  alert(\"Hello!\");\n};\n*/!*\n\nuser.sayHi(); // Hello!\n```\n\nDi sini kita hanya menggunakan sebuah fungsi ekspresi untuk membuat fungsi dan menugaskannya ke properti `user.sayHi` pada objek.\n\nKemudian kita memanggil fungsi tersebut. Kini \"pengguna\" bisa berbicara!\n\nSebuah fungsi yang mana merupakan properti dari sebuah objek disebut sebagai *metode*-nya.\n\nJadi, di sini kita memiliki sebuah metode `sayHi` dari objek`user`.\n\nTentu saja, kita bisa menggunakan sebuah fungsi sebagai sebuah metode pra-deklarasi (*pre-declared*), seperti beriktu:\n\n```js run\nlet user = {\n  // ...\n};\n\n*!*\n// pertama, deklarasi\nfunction sayHi() {\n  alert(\"Hello!\");\n};\n\n// lalu tambbahkan sebagai sebuah metodes\nuser.sayHi = sayHi;\n*/!*\n\nuser.sayHi(); // Hello!\n```\n\n```smart header=\"Object-oriented programming\"\nKetika kita menulis kode kita menggunakan objek-objek untuk merepresentasikan benda, itulah yang disebut sebagai [object-oriented programming](https://en.wikipedia.org/wiki/Object-oriented_programming), disingkat menjadi: \"OOP\".\n\nOOP adalah hal besar, sebuah sains yang sangat menarik. Bagaimana cara memilih entitas yang benar? bagaimana cara mengorganisir interaksi diantara mereka? Itulah arsitektur, dan terdapat buku yang bagus untuk topik itu, seperti \"Design Patterns: Elements of Reusable Object-Oriented Software\" oleh E. Gamma, R. Helm, R. Johnson, J. Vissides atau \"Object-Oriented Analysis and Design with Applications\" by G. Booch, and more.\n```\n### Metode ringkas\n\nTerdapat sebuah sintaks yang lebih pendek untuk metode-metode dalams sebuah penulisan (kode) objek:\n\n```js\n// objek-objek ini sama\n\nuser = {\n  sayHi: function() {\n    alert(\"Hello\");\n  }\n};\n\n// metode ringkas terlihat lebih bagus, kan?\nuser = {\n*!*\n  sayHi() { // sama seperti \"sayHi: function()\"\n*/!*\n    alert(\"Hello\");\n  }\n};\n```\n\nSeperti yang didemonstrasikan, kita bisa mengabaikan `\"function\"` dan hanya menuliskan `sayHi()`.\n\nSebenarnya, notasi-notasi tersebut tidak sepenuhnya sama. Ada beberapa perbedaan kecil yang berhubungan dengan *object inheritance* atau pewarisan objek (akan dibahas nanti), tetapi untuk sekarang hal-hal tersebut tidak terlalu penting. Dalam hampir kebanyakan kasus sintaks ringkas lebih disukai.\n\n## \"this\" dalam metode\n\nSudah umum bahwa sebuah metode objek perlu untuk mengakses informasi yang disimpan dalam objek untuk melakukan tugasnya.\n\nContohnya, kode di dalam `user.sayHi()` bisa jadi membutuhkan nama dari `user`.\n\n**Untuk mengakses objek yang bersangkutan, sebuah metode dapat menggunakan kata kunci `this`.**\n\nNilai dari `this` adalah objek \"sebelum (tanda) titik\", yang mana sebelumnya memanggil metode tersebut.\n\nSebagai contoh:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    // \"this\" adalah \"objek yang sekarang\"\n    alert(this.name);\n*/!*\n  }\n\n};\n\nuser.sayHi(); // John\n```\n\nDi sini selama ekseskusi `user.sayHi()`, nilai dari `this` akan menjadi `user`.\n\nSecara teknis, memungkingkan juga untuk mengakses objek tanpa `this`, dengan cara mereferensikannya melalui variabel luar:\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    alert(user.name); // menggunakan \"user\" ketimbang \"this\"\n*/!*\n  }\n\n};\n```\n\n...Namun kode yang demikian tidak dapat diandalkan. Jika kita memilih untuk menyalin `user` ke sebuah variabel lain, misalnya `admin = user` dan menimpa `user` dengan hal lain, akhirnya malah akan mengakses objek yang salah.\n\nHal tersebut dicontohkan seperti berikut ini:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n  sayHi() {\n*!*\n    alert( user.name ); // akan mengarah ke sebuah error\n*/!*\n  }\n\n};\n\n\nlet admin = user;\nuser = null; // timpa/overwrite agar terlihat lebih jelas\n\nadmin.sayHi(); // Uups! dalam sayHi(), nama yang lama sedang digunakan! error!\n```\n\nJika kita menggunakan `this.name` ketimbang `user.name` dalam `alert`, maka kodenya akhirnya berfungsi.\n\n## \"this\" tidak ditemukan\n\nDalam JavaScript, kata kunci `this` berperilaku tidak seperti kebanyak bahasa pemrograman lainnya. 'this' juga bisa digunakan dalam fungsi apapun.\n\nTidak ada *syntax error* dalam contoh berikut ini:\n\n```js\nfunction sayHi() {\n  alert( *!*this*/!*.name );\n}\n```\n\nNilai dari `this` dievaluasi selama proses *run-time*, tergantung dari konteksnya.\n\nContohnya, di sini fungsi yang sama ditugaskan pada dua objek yang berbeda dan memiliki \"this\" dalam pemanggilannya:\n\n```js run\nlet user = { name: \"John\" };\nlet admin = { name: \"Admin\" };\n\nfunction sayHi() {\n  alert( this.name );\n}\n\n*!*\n// menggunakan fungsi yang sama dalam dua objek\nuser.f = sayHi;\nadmin.f = sayHi;\n*/!*\n\n// panggilan-panggilan ini memiliki this yang berbeda\n// \"this\" dalam fungsi adalah objek \"sebelum tanda titik\"\nuser.f(); // John  (this == user)\nadmin.f(); // Admin  (this == admin)\n\nadmin['f'](); // Admin (tanda titik atau kurung siku mengakses metode tersebut – bukan masalah)\n```\n\nAturannya sederhana: jika `obj.f()` dipanggil, maka `this` adalah `obj` selama pemanggilan `f`. Jadi antara `user` atau `admin` pada contoh di atas.\n\n````smart header=\"Pemanggilan tanpa sebuah objek: `this == undefined`\"\nKita bahkan bisa memanggil fungsi tanpa sebuah objek sama sekali:\n\n```js run\nfunction sayHi() {\n  alert(this);\n}\n\nsayHi(); // undefined\n```\n\nDalam kasus ini `this` adalah `undefined` di mode *strict*. Jika kita coba untuk mengakses `this.name`, akan menghasilkan sebuah error.\n\nDalam mode *non-strict* nilai dari `this` dalam kasus demikian akan menjadi *objek global* (`window` dalam sebuah peramban, kita akan membahasnya lebih lanjut dalam bab [](info:global-object)). Ini adalah sebuah perilaku historis yang dibenahi oleh `\"use strict\"`.\n\nBiasanya panggilan yang demikian adalah sebuah kesalahan *programming*. Jika terdapat `this` dalam sebuah fungsi, `this` tersebut kemungkinan besar akan dipanggil dalam konteks sebuah objek.\n````\n\n```smart header=\"Akibat dari `this` yang tidak terikat\"\nJika kamu berasal dari bahasa pemrograman lain, mungkin saja kamu menggunakan gagasan sebuah \"pengikatan (bound) `this`\", dimana metode-metode didefinisikan dalam sebuah objek selalu memiliki `this` yang merujuk pada objek itu.\n\nDalam JavaScript `this` itu \"bebas\", nilainya dieveluasi saat waktu pemanggilan dan tidak tergantung pada dimana metode tersebut di deklarasikan, namun lebih pada objek apa yang berada \"sebelum (tanda) titik\".\n\nKonsep run-time mengeveluasi `this` memiliki kelebihan dan kekurangan sendiri. Di satu sisi, sebuah fungsi bisa digunakan ulang untuk objek-objek yang berbeda. Pada sisi sebaliknya, fleksibilitas yang besar membuat lebih banyak kemungkinan adanya kesalahan-kesalahan.\n\nDi sini posisi kita tidak untuk menghakimi apakah pilihan rancangan bahasa pemrograman ini baik atau buruk. Kita akan mengerti bagaimana bekerja dengan hal itu, serta bagaimana cara mendapatkan keuntungan dari hal tersebut dan menghindari adanya masalah.\n````\n\n## Fungsi arrow tidak memiliki \"this\"\n\nFungsi-fungsi *arrow* itu istimewa: fungsi tersebut tidak memiliki `this` \"milik fungsi itu sendiri\". Jika kita mereferensikan `this` dari fungsi demikian, hal itu didapat dari fungsi \"normal\" di luar.\n\nFor instance, here `arrow()` uses `this` from the outer `user.sayHi()` method:\n\n```js run\nlet user = {\n  firstName: \"Ilya\",\n  sayHi() {\n    let arrow = () => alert(this.firstName);\n    arrow();\n  }\n};\n\nuser.sayHi(); // Ilya\n```\n\nItulah fitur istimewa dari fungsi *arrow*, fitur berguna ketika kita benar-benar tidak ingin memiliki sebuah `this` yang terpisah, namun kita mengambilnya dari luar lingkung tersebut. Selanjutnya dalam bab <info:arrow-functions> kita akan mempelajari lebih dalam mengenai fungsi *arrow*.\n\n\n## Ringkasan\n\n- Fungsi-fungsi yang disimpan dalam properti objek disebut sebagai \"metode\".\n- Metode membuat objek dapat \"bertindak\" seperti `object.doSomething()`.\n- Metode bisa mereferensikan ke objek sebagai `this`.\n\nNilai dari `this` didefiniskan saat *run-time*.\n- Ketika sebuah fungsi dideklarasikan, fungsi tersebut bisa menggunakan `this`, tetapi `this` tersebut tidak memiliki nilai sampai fungsi tersebut dipanggil.\n- Sebuah fungsi bisa disalin di antara objek-objek.\n- Ketika sebuah fungsi dipanggil dalam sintaks \"metode\": `object.method()`, nilai `this` selama pemanggilan adalah `object`.\n\nMohon diingat bahwa fungsi *arrow* itu istimewa: fungsi tersebut tidak memiliki `this`. Ketika `this` diakses di dalam sebuah fungsi *arrow*, `this` itu diambil dari luar.\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/solution.md",
    "content": "Ya, hal itu memungkinkan.\n\nJika sebuah fungsi mengembalikan sebuah objek lalu `new` mengembalikan objek tersebut sebagai ganti `this`.\n\nJadi fungsi tersebut dapat, misalnya, mengembalikan objek `obj` yang secara eksternal didefinisikan sama:\n\n```js run no-beautify\nlet obj = {};\n\nfunction A() { return obj; }\nfunction B() { return obj; }\n\nalert( new A() == new B() ); // true\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/1-two-functions-one-object/task.md",
    "content": "importance: 2\n\n---\n\n# Dua fungsi – satu objek\n\nApakah mungkin untuk membuat fungsi `A` dan fungsi `B` seperti `new A()==new B()`?\n\n```js no-beautify\nfunction A() { ... }\nfunction B() { ... }\n\nlet a = new A;\nlet b = new B;\n\nalert( a == b ); // true\n```\n\nJika bisa, berikan contoh kodenya.\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/solution.js",
    "content": "function Calculator() {\n\n  this.read = function() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  };\n\n  this.sum = function() {\n    return this.a + this.b;\n  };\n\n  this.mul = function() {\n    return this.a * this.b;\n  };\n}"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/_js.view/test.js",
    "content": "\ndescribe(\"calculator\", function() {\n  let calculator;\n  before(function() {\n    sinon.stub(window, \"prompt\")\n\n    prompt.onCall(0).returns(\"2\");\n    prompt.onCall(1).returns(\"3\");\n\n    calculator = new Calculator();\n    calculator.read();\n  });\n  \n  it(\"the read method asks for two values using prompt and remembers them in object properties\", function() {\n    assert.equal(calculator.a, 2);\n    assert.equal(calculator.b, 3);\n  });\n\n  it(\"when 2 and 3 are entered, the sum is 5\", function() {\n    assert.equal(calculator.sum(), 5);\n  });\n\n  it(\"when 2 and 3 are entered, the product is 6\", function() {\n    assert.equal(calculator.mul(), 6);\n  });\n\n  after(function() {\n    prompt.restore();\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/solution.md",
    "content": "```js run demo\nfunction Calculator() {\n\n  this.read = function() {\n    this.a = +prompt('a?', 0);\n    this.b = +prompt('b?', 0);\n  };\n\n  this.sum = function() {\n    return this.a + this.b;\n  };\n\n  this.mul = function() {\n    return this.a * this.b;\n  };\n}\n\nlet calculator = new Calculator();\ncalculator.read();\n\nalert( \"Sum=\" + calculator.sum() );\nalert( \"Mul=\" + calculator.mul() );\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/2-calculator-constructor/task.md",
    "content": "importance: 5\n\n---\n\n# Buat Kalkulator baru\n\nBuatlah sebuah fungsi konstruktor `Calculator` yang membuat objek dengan 3 method:\n\n- `read()` tanyakan dua nilai menggunakan `prompt dan masukan mereka kedalam properti objek.\n- `sum()` mengembalikan jumlah dari properti-properti.\n- `mul()` mengembalikan perkalian produk dari properti-properti.\n\nContoh:\n\n```js\nlet calculator = new Calculator();\ncalculator.read();\n\nalert( \"Sum=\" + calculator.sum() );\nalert( \"Mul=\" + calculator.mul() );\n```\n\n[demo]\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/solution.js",
    "content": "function Accumulator(startingValue) {\n  this.value = startingValue;\n\n  this.read = function() {\n    this.value += +prompt('How much to add?', 0);\n  };\n\n}\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/_js.view/test.js",
    "content": "describe(\"Accumulator\", function() {\n\n  beforeEach(function() {\n    sinon.stub(window, \"prompt\")\n  });\n\n  afterEach(function() {\n    prompt.restore();\n  });\n\n  it(\"initial value is the argument of the constructor\", function() {\n    let accumulator = new Accumulator(1);\n\n    assert.equal(accumulator.value, 1);\n  });\n\n  it(\"after reading 0, the value is 1\", function() {\n    let accumulator = new Accumulator(1);\n    prompt.returns(\"0\");\n    accumulator.read();\n    assert.equal(accumulator.value, 1);\n  });\n\n  it(\"after reading 1, the value is 2\", function() {\n    let accumulator = new Accumulator(1);\n    prompt.returns(\"1\");\n    accumulator.read();\n    assert.equal(accumulator.value, 2);\n  });\n});\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/solution.md",
    "content": "\n\n```js run demo\nfunction Accumulator(startingValue) {\n  this.value = startingValue;\n\n  this.read = function() {\n    this.value += +prompt('How much to add?', 0);\n  };\n\n}\n\nlet accumulator = new Accumulator(1);\naccumulator.read();\naccumulator.read();\nalert(accumulator.value);\n```\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/3-accumulator/task.md",
    "content": "importance: 5\n\n---\n\n# Membuat Akumulator baru\n\nBuatlah sebuah fungsi konstruktor `Accumulator(startingValue)`.\n\nObjek yang dibuat fungsi tersebut harus:\n\n- Menyimpan \"nilai yang sekarang\" dalam `value` properti. Nilai awal diatur menjadi argumen konstruktor `startingValue`.\n- Metode `read()` harus menggunakan `prompt` untuk membaca sebuah angka dan menambahkannya ke `value`.\n\nDalam kata lain, properti `value` adalah hasil penjumlahan dari semua nilai yang dimasukkan oleh pengguna dengan nilai awal `startingValue`.\n\nBerikut ini contoh kodenya:\n\n```js\nlet accumulator = new Accumulator(1); // nilai awal 1\n\naccumulator.read(); // menambahkan nilai yang dimasukkan oleh pengguna\naccumulator.read(); // menambahkan nilai yang dimasukkan oleh pengguna\n\nalert(accumulator.value); // menampilkan jumlah dari kedua nilai\n```\n\n[demo]\n"
  },
  {
    "path": "1-js/04-object-basics/06-constructor-new/article.md",
    "content": "# Konstruktor, operator \"new\"\n\nSintaks reguler `{...}` memungkinkan kita untuk membuat satu objek. Tapi seringkali kita perlu untuk membuat banyak objek-objek serupa, seperti pengguna atau *item* menu berganda dan sebagainya.\n\nHal tersebut dapat diselesaikan dengan menggunakan fungsi konstruktor dan operator `\"new\"`.\n\n## Fungsi konstruktor\n\nFungsi konstruktor secara teknis adalah fungsi biasa. Terdapat dua persetujuan sebelumnya, yakni:\n\n1. Fungsi tersebut diberi nama dengan huruf kapital terlebih dulu.\n2. Fungsi tersebut harus dieksekusi dengan hanya menggunakan operator `\"new\"`.\n\nSebagai contoh:\n\n```js run\nfunction User(name) {\n  this.name = name;\n  this.isAdmin = false;\n}\n\n*!*\nlet user = new User(\"Jack\");\n*/!*\n\nalert(user.name); // Jack\nalert(user.isAdmin); // false\n```\n\nKetika sebuah fungsi dieksekusi dengan menggunakan `new`, fungsi tersebut melakukan tahapan-tahapan berikut ini:\n\n1. Sebuah objek kosong yang dibuat dan diserahkan ke `this`.\n2. Bagian utama fungsi tersebut bereksekusi. Biasanya memodifikasi `this`, menambahkan properti baru ke `this`.\n3. Nilai dari `this` dikembalikan.\n\nDengan kata lain, `new User(...)` berjalan seperti ini:\n\n```js\nfunction User(name) {\n*!*\n  // this = {};  (secara implisit)\n*/!*\n\n  // menambahkan properti ke this\n  this.name = name;\n  this.isAdmin = false;\n\n*!*\n  // mengembalikan this;  (secara implisit)\n*/!*\n}\n```\n\nJadi `let user = new User(\"Jack\")` memberikan hasil yang sama seperti halnya:\n\n```js\nlet user = {\n  name: \"Jack\",\n  isAdmin: false\n};\n```\n\nSekarang jika kita ingin membuat *user* lain, kita bisa memanggil `new User(\"Ann\")`, `new User(\"Alice\")` dan seterusnya. Lebih pendek penulisannya daripada menulis langsung sintaks baru setiap ingin membuat *user* baru, serta lebih mudah dibaca.\n\nItulah tujuan utama dari konstruktor -- untuk mengimplementasikan kode pembuatan objek yang dapat dipakai ulang (*reusable*).\n\nMari ingat sekali lagi -- secara teknis, fungsi apapun dapat digunakan sebagai sebuah konstruktor. Hal tersebut berarti: fungsi apapun dapat dijalankan dengan `new`, dan bisa mengeksekusi algoritma di atas. \"Huruf kapital dulu\" adalah kesepakatan umum, untuk membuatnya lebih jelas bahwa sebuah fungsi untuk dijalankan dengan `new`.\n\n````smart header=\"function() { ... } baru\"\nJika kita memiliki banyak baris kode tentang pembuatan sebuah objek tunggal yang kompleks, kita dapat membungkus kode tersebut dalam fungsi konstruktor, seperti ini:\n\n```js\n// create a function and immediately call it with new\nlet user = new function() { \n  this.name = \"John\";\n  this.isAdmin = false;\n\n  // ...kode lain untuk pembuatan user\n  // bisa jadi lebih rumit logika dan pernyataannya\n  // variabel lokal dan lain-lain.\n};\n```\n\nKonstruktor tersebut tidak dapat dipanggil lagi, karena tidak disimpan dimanapun, hanya dibuat dan dipanggil. Jadi trik ini ditujukan untuk mengenkapsulasi kode yang mengonstruksi objek tunggal, tanpa penggunaan di masa yang akan datang.\n````\n\n## Constructor mode test: new.target\n\n```smart header=\"Pembahasan tingkat lanjut\"\nSintaks dari bagian ini jarang digunakan, lewati saja kecuali kamu ingin mengetahui semuanya.\n```\n\nDi dalam sebuah fungsi, kita dapat memeriksa apakah fungsi tersebut dipanggil dengan atau tanpa `new`, dengan cara menggunakan sebuah properti khusus `new.target`.\n\nFungsi tersebut kosong untuk panggilan-panggilan reguler dan menyamai fungsi jika dipanggil dengan `new`:\n\n```js run\nfunction User() {\n  alert(new.target);\n}\n\n// tanpa \"new\":\n*!*\nUser(); // undefined\n*/!*\n\n// dengan \"new\":\n*!*\nnew User(); // fungsi User { ... }\n*/!*\n```\n\nSintaks itu dapat digunakan di dalam fungsi tersebut untuk mengetahui apakah fungsi dipanggil dengan `new`, \"dalam mode konstruktor\", atau tanpa `new`, \"dalam mode reguler\".\n\nKita juga bisa membuat baik panggilan dengan `new` serta panggilan reguler untuk melakukan hal yang sama, seperti ini:\n\n```js run\nfunction User(name) {\n  if (!new.target) { // jika kamu menjalankanku tanpa new\n    return new User(name); // ...aku akan menambahkan new untukmu\n  }\n\n  this.name = name;\n}\n\nlet john = User(\"John\"); // mengarahkan ulang panggilan ke User baru\nalert(john.name); // John\n```\n\nPendekatan ini terkadang digunakan dalam *library* untuk membuat sintaks lebih fleksibel. Jadi orang-orang bisa memanggil fungsi dengan atau tanap `new`, dan masih bisa berfungsi.\n\nMungkin saja bukanlah hal baik untuk menggunakan pendekatan tersebut dimana saja, karena melewatkan `new` membuat kurang jelas sintaks atau kode yang sedang berjalan. Dengan `new` kita semua tahu bahwa objek baru sedang dibuat.\n\n## Hasil *return* dari konstruktor\n\nBiasanya, konstruktor tidak memiliki sebuah pernyataan `return`. Tugas konstruktor adalah untuk menulis semua hal-hal yang dibutuhkan ke dalam `this`, dan hal tersebut secara otomatis menjadi hasil.\n\nTetapi jika ada sebuah pernyataan `return`, maka aturannya sederhana:\n\n- Jika `return` dipanggil dengan sebuah objek, maka objek tersebut dikembalikan sebagai ganti `this`.\n- Jika `return` dipanggil dengan sebuah *primitive*, panggilan tersebut diabaikan.\n\nDalam kata lain, `return` dengan sebuah objek mengembalikan objek itu, dalam semua kasus lainnya `this` dikembalikan.\n\nSebagai contoh, di sini `return` mengambil alih `this` dengan cara mengembalikan sebuah objek:\n\n```js run\nfunction BigUser() {\n\n  this.name = \"John\";\n\n  return { name: \"Godzilla\" };  // <-- mengembalikan objek this\n}\n\nalert( new BigUser().name );  // Godzilla, dapat objeknya\n```\n\nDan berikut ini adalah sebuah contoh dengan sebuah `return` kosong (atau bisa kita tempatkan dengan sebuah *primitive* setelahnya, tidak dipermasalahkan):\n\n```js run\nfunction SmallUser() {\n\n  this.name = \"John\";\n\n  return; // <-- mengembalikan this\n}\n\nalert( new SmallUser().name );  // John\n```\n\nBiasanya konstruktor tidak memiliki sebuah pernyataan `return`. Di sini kita menyebutkan perilaku khusus dengan cara mengembalikan objek dengan tujuan utamanya yakni hanya untuk melengkapi saja.\n\n````smart header=\"Mengabaikan parentheses\"\nOmong-omong, kita bisa mengabaikan parentheses setelah `new`, jika tidak memiliki argumen:\n\n```js\nlet user = new User; // <-- tanpa parentheses\n// sama seperti\nlet user = new User();\n```\n\nMengabaikan parentheses di sini tidak dipandang sebagai sebuah \"gaya yang baik\", tetapi sintaks tersebut diperbolehkan oleh spesifikasi.\n````\n\n## Metode dalam konstruktor\n\nMenggunakan fungsi-fungsi konstruktor untuk membuat objek memberikan sebuah fleksibilitas yang amat baik. Fungsi konstruktor bisa jadi memiliki parameter yang mendefinisikan bagaimana cara untuk mengonstruksi objek tersebut, serta hal apa yang dimasukkan ke objek tersebut.\n\nTentu saja, kita bisa menambahkan ke `this` tidak hanya properti, tapi metode juga.\n\nSebagai contoh, `new User(name)` di bawah membuat sebuah objek dengan `name` yang diberikan dan metode `sayHi`:\n\n```js run\nfunction User(name) {\n  this.name = name;\n\n  this.sayHi = function() {\n    alert( \"My name is: \" + this.name );\n  };\n}\n\n*!*\nlet john = new User(\"John\");\n\njohn.sayHi(); // My name is: John\n*/!*\n\n/*\njohn = {\n   name: \"John\",\n   sayHi: function() { ... }\n}\n*/\n```\n\nUntuk membuat objek yang kompleks, ada sintaks tingkat lanjut, yaitu [classes](info:classes), yang akan kita bahas nanti.\n\n## Ringkasan\n\n- Fungsi konstruktor, atau secara singkat, konstruktor, adalah fungsi reguler, tetapi ada sebuah kesepakatan umum untuk memberi nama konstruktor dengan huruf kapital terlebih dahulu.\n- Fungsi konstruktor seharusnya hanya bisa dipanggil menggunakan `new`. Panggilan yang demkian berarti sebuah pembuatan `this` kosong pada awalnya dan mengembalikan `this` yang sudah berisi di akhir.\n\nKita bisa menggunakan fungsi konstruktor untuk membuat objek-objek serupa sekaligus.\n\nJavaScript menyediakan fungsi konstruktor untuk banyak objek bawaan di bahasa pemrograman: seperti `Date` untuk penanggalan, `Set` untuk kumpulan/rangkaian dan banyak lainnya yang akan kita pelajari.\n\n```smart header=\"Objek, kita akan kembali!\"\nDalam bab ini kita hanya membahas dasar-dasar tentang objek dan konstruktor. Objek dan konstruktor adalah dasar penting untuk mempelajari lebih banyak tentang tipe data dan fungsi dalam bab-bab selanjutnya.\n\nSetelah kita mempelajari itu, kita kembali ke objek dan membahas objek lebih mendalam lagi di bab <info:prototypes> dan <info:classes>.\n```\n"
  },
  {
    "path": "1-js/04-object-basics/07-optional-chaining/article.md",
    "content": "\n# Optional chaining '?.'\n\n[recent browser=\"new\"]\n\nThe optional chaining `?.` is a safe way to access nested object properties, even if an intermediate property doesn't exist.\n\n## The \"non-existing property\" problem\n\nIf you've just started to read the tutorial and learn JavaScript, maybe the problem hasn't touched you yet, but it's quite common.\n\nAs an example, let's say we have `user` objects that hold the information about our users.\n\nMost of our users have addresses in `user.address` property, with the street `user.address.street`, but some did not provide them.\n\nIn such case, when we attempt to get `user.address.street`, and the user happens to be without an address, we get an error:\n\n```js run\nlet user = {}; // a user without \"address\" property\n\nalert(user.address.street); // Error!\n```\n\nThat's the expected result. JavaScript works like this. As `user.address` is `undefined`, an attempt to get `user.address.street` fails with an error.\n\nIn many practical cases we'd prefer to get `undefined` instead of an error here (meaning \"no street\").\n\n...And another example. In the web development, we can get an object that corresponds to a web page element using a special method call, such as `document.querySelector('.elem')`, and it returns `null` when there's no such element.\n\n```js run\n// document.querySelector('.elem') is null if there's no element\nlet html = document.querySelector('.elem').innerHTML; // error if it's null\n```\n\nOnce again, if the element doesn't exist, we'll get an error accessing `.innerHTML` of `null`. And in some cases, when the absence of the element is normal, we'd like to avoid the error and just accept `html = null` as the result.\n\nHow can we do this?\n\nThe obvious solution would be to check the value using `if` or the conditional operator `?`, before accessing its property, like this:\n\n```js\nlet user = {};\n\nalert(user.address ? user.address.street : undefined);\n```\n\nIt works, there's no error... But it's quite inelegant. As you can see, the `\"user.address\"` appears twice in the code. For more deeply nested properties, that becomes a problem as more repetitions are required.\n\nE.g. let's try getting `user.address.street.name`.\n\nWe need to check both `user.address` and `user.address.street`:\n\n```js\nlet user = {}; // user has no address\n\nalert(user.address ? user.address.street ? user.address.street.name : null : null);\n```\n\nThat's just awful, one may even have problems understanding such code.\n\nDon't even care to, as there's a better way to write it, using the `&&` operator:\n\n```js run\nlet user = {}; // user has no address\n\nalert( user.address && user.address.street && user.address.street.name ); // undefined (no error)\n```\n\nAND'ing the whole path to the property ensures that all components exist (if not, the evaluation stops), but also isn't ideal.\n\nAs you can see, property names are still duplicated in the code. E.g. in the code above, `user.address` appears three times.\n\nThat's why the optional chaining `?.` was added to the language. To solve this problem once and for all!\n\n## Optional chaining\n\nThe optional chaining `?.` stops the evaluation if the value before `?.` is `undefined` or `null` and returns `undefined`.\n\n**Further in this article, for brevity, we'll be saying that something \"exists\" if it's not `null` and not `undefined`.**\n\nIn other words, `value?.prop`:\n- works as `value.prop`, if `value` exists,\n- otherwise (when `value` is `undefined/null`) it returns `undefined`.\n\nHere's the safe way to access `user.address.street` using `?.`:\n\n```js run\nlet user = {}; // user has no address\n\nalert( user?.address?.street ); // undefined (no error)\n```\n\nThe code is short and clean, there's no duplication at all.\n\nReading the address with `user?.address` works even if `user` object doesn't exist:\n\n```js run\nlet user = null;\n\nalert( user?.address ); // undefined\nalert( user?.address.street ); // undefined\n```\n\nPlease note: the `?.` syntax makes optional the value before it, but not any further.\n\nE.g. in `user?.address.street.name` the `?.` allows `user` to safely be `null/undefined` (and returns `undefined` in that case), but that's only for `user`. Further properties are accessed in a regular way. If we want some of them to be optional, then we'll need to replace more `.` with `?.`.\n\n```warn header=\"Don't overuse the optional chaining\"\nWe should use `?.` only where it's ok that something doesn't exist.\n\nFor example, if according to our coding logic `user` object must exist, but `address` is optional, then we should write `user.address?.street`, but not `user?.address?.street`.\n\nSo, if `user` happens to be undefined due to a mistake, we'll see a programming error about it and fix it. Otherwise, coding errors can be silenced where not appropriate, and become more difficult to debug.\n```\n\n````warn header=\"The variable before `?.` must be declared\"\nIf there's no variable `user` at all, then `user?.anything` triggers an error:\n\n```js run\n// ReferenceError: user is not defined\nuser?.address;\n```\nThe variable must be declared (e.g. `let/const/var user` or as a function parameter). The optional chaining works only for declared variables.\n````\n\n## Short-circuiting\n\nAs it was said before, the `?.` immediately stops (\"short-circuits\") the evaluation if the left part doesn't exist.\n\nSo, if there are any further function calls or side effects, they don't occur.\n\nFor instance:\n\n```js run\nlet user = null;\nlet x = 0;\n\nuser?.sayHi(x++); // no \"sayHi\", so the execution doesn't reach x++\n\nalert(x); // 0, value not incremented\n```\n\n## Other variants: ?.(), ?.[]\n\nThe optional chaining `?.` is not an operator, but a special syntax construct, that also works with functions and square brackets.\n\nFor example, `?.()` is used to call a function that may not exist.\n\nIn the code below, some of our users have `admin` method, and some don't:\n\n```js run\nlet userAdmin = {\n  admin() {\n    alert(\"I am admin\");\n  }\n};\n\nlet userGuest = {};\n\n*!*\nuserAdmin.admin?.(); // I am admin\n*/!*\n\n*!*\nuserGuest.admin?.(); // nothing (no such method)\n*/!*\n```\n\nHere, in both lines we first use the dot (`userAdmin.admin`) to get `admin` property, because we assume that the user object exists, so it's safe read from it.\n\nThen `?.()` checks the left part: if the admin function exists, then it runs (that's so for `userAdmin`). Otherwise (for `userGuest`) the evaluation stops without errors.\n\nThe `?.[]` syntax also works, if we'd like to use brackets `[]` to access properties instead of dot `.`. Similar to previous cases, it allows to safely read a property from an object that may not exist.\n\n```js run\nlet key = \"firstName\";\n\nlet user1 = {\n  firstName: \"John\"\n};\n\nlet user2 = null; \n\nalert( user1?.[key] ); // John\nalert( user2?.[key] ); // undefined\n```\n\nAlso we can use `?.` with `delete`:\n\n```js run\ndelete user?.name; // delete user.name if user exists\n```\n\n````warn header=\"We can use `?.` for safe reading and deleting, but not writing\"\nThe optional chaining `?.` has no use at the left side of an assignment.\n\nFor example:\n```js run\nlet user = null;\n\nuser?.name = \"John\"; // Error, doesn't work\n// because it evaluates to undefined = \"John\"\n```\n\nIt's just not that smart.\n````\n\n## Summary\n\nThe optional chaining `?.` syntax has three forms:\n\n1. `obj?.prop` -- returns `obj.prop` if `obj` exists, otherwise `undefined`.\n2. `obj?.[prop]` -- returns `obj[prop]` if `obj` exists, otherwise `undefined`.\n3. `obj.method?.()` -- calls `obj.method()` if `obj.method` exists, otherwise returns `undefined`.\n\nAs we can see, all of them are straightforward and simple to use. The `?.` checks the left part for `null/undefined` and allows the evaluation to proceed if it's not so.\n\nA chain of `?.` allows to safely access nested properties.\n\nStill, we should apply `?.` carefully, only where it's acceptable that the left part doesn't exist. So that it won't hide programming errors from us, if they occur."
  },
  {
    "path": "1-js/04-object-basics/08-symbol/article.md",
    "content": "\n# Tipe simbol\n\nMenurut spesifikasi spesifikasi, properti-properti kunci objek bisa saja bertipe *string*, atau bertipe simbol. Bukan angka (*number*), bukan *boolean*, hanya *string* atau simbol-simbol, kedua tipe ini.\n\nHingga kini kita telah menggunakan *string* saja. Mari kita lihat keuntungan-keuntungan apa saja dari simbol yang bisa diberikan ke kita.\n\n## Simbol-simbol\n\nSebuah \"simbol\" merepresentasikan sebuah pengidentifikasi yang unik.\n\nNilai dari tipe ini dapat dibuat menggunakan `Symbol()`:\n\n```js\n// id adalah sebuah simbol baru\nlet id = Symbol();\n```\n\nSelama penyusunan, kita bisa memberikan simbol sebuah deskripsi (juga disebut sebagai nama simbol), kebanyakan berguna untuk tujuan-tujuan *debugging*:\n\n```js\n// id adalah simbol dengan deskripsi \"id\"\nlet id = Symbol(\"id\");\n```\n\nSimbol-simbol sudah pasti unik. Bahkan jika kita membuat banyak simbol dengan deskripsi yang, mereka memiliki nilai-nilai yang berbeda. Deskripsi hanyalah sebuah label yang tidak mempengaruhi apapun.\n\nSebagai contoh, berikut ini ada dua simbol dengan deskripsi yang sama -- keduanya tidak sama:\n\n```js run\nlet id1 = Symbol(\"id\");\nlet id2 = Symbol(\"id\");\n\n*!*\nalert(id1 == id2); // false\n*/!*\n```\n\nJika kamu tidak asing dengan Ruby atau bahasa pemrograman lain yang juga memiliki hal seperti \"simbol\" -- tolong jangan sampai keliru. Simbol-simbol (di) JavaScript itu berbeda.\n\n````warn header=\"Simbol-simbol tidak dikonversi otomatis menjadi string\"\nKebanyakan nilai-nilai dalam JavaScript mendukung konversi implisit menjadi sebuah string. Contohnya, kita bisa memberi `alert` pada hampir nilai apapun, dan masih akan berfungsi. Simbol itu istimewa. Mereka tidak terkonversi otomatis.\n\nSebagai contoh, `alert` ini akan memunculkan sebuah error:\n\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id); // TypeError: Cannot convert a Symbol value to a string\n*/!*\n```\n\nHal tersebut adalah sebuah \"garda bahasa pemrograman\" untuk menghadapi adanya kekacauan, karena string dan simbol itu berbeda secara fundamental dan sudah seharusnya tidak akan terkonversi dari satu ke lainnya secara tidak sengaja.\n\nJika kita benar-benar ingin menunjukkan sebuah simbol, kita perlu secara eskplisit memanggil `.toString()` sintaks tersebut, seperti berikut ini:\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id.toString()); // Symbol(id), sekarang berfungsi\n*/!*\n```\n\nAtau mengambil properti `symbol.description` untuk menunjuukan deskripsinya saja:\n```js run\nlet id = Symbol(\"id\");\n*!*\nalert(id.description); // id\n*/!*\n```\n\n````\n\n## Properti \"tersembunyi\" (*hidden*)\n\nSimbol memungkinkan kita untuk membuat properti-properti yang \"tersembunyi\" (*hidden*) dari sebuah objek, yang mana tidak akan ada bagian lain dari kode yang bisa mengakses atau meng-*overwrite* tanpa sengaja.\n\nSebagai contoh, jika kita bekerja dengan objek-objek `user`, yang dimiliki oleh sebuah kode pihak ketiga. Kita akan menambahkan pengidentifikasi pada objek-objek tersebut.\n\nMari gunakan sebuah kunci simbol untuk hal tersebut:\n\n```js run\nlet user = { // dimiliki oleh kode lainyya\n  name: \"John\"\n};\n\nlet id = Symbol(\"id\");\n\nuser[id] = 1;\n\nalert( user[id] ); // kita bisa mengakses data menggunakan simbol sebagai kunci\n```\n\nApa keuntungan dari menggunakan `Symbol(\"id\")` daripada sebuah *string* `\"id\"`?\n\nSebagaimana objek-objek `user` adalah milik kode lain, dan kode itu juga berfungsi dengan objek-objek tadi, kita seharusnya tidak hanya menambahkan ruang apapun di situ. Hal tersebut tidak aman. Tetapi sebuah simbol tidak bisa diakses tanpa sengaja, kode pihak ketiga bahkan tidak akan melihatnya, jadi mungkin tidak masalah jika demikian.\n\nJuga, bayangkan *script* lain ingin memiliki pengidentifikasi sendiri dalam objek `user`, untuk tujuannya masing-masing. Hal tersebut bisa saja *library* JavaScript lainnya, jadi *script-script* tersebut benar-benar tidak menyadari satu sama lainnya.\n\nKemudian *script* tersebut bisa membuat `Symbol(\"id\")`-nya sendiri, seperti berikut ini:\n\n```js\n// ...\nlet id = Symbol(\"id\");\n\nuser[id] = \"Their id value\";\n```\n\nTidak ada konflik antara pengidentifikasi kita dengan pengidentifikasi mereka, karena simbol selalu berebeda, bahkan jika simbol-simbol itu memiliki nama yang sama.\n\n...Tapi jika kita menggunakan sebuah *string* `\"id\"` bukan sebuah simbol untuk tujuan yang sama, dengan demikian *akan menjadi* sebuah konflik:\n\n```js\nlet user = { name: \"John\" };\n\n// Script kita menggunakan properti \"id\"\nuser.id = \"Our id value\";\n\n// ...Script lain juga menginginkan \"id\" untuk tujuannya sendiri...\n\nuser.id = \"Their id value\"\n// Boom! properti tertimpa/overwrite oleh script lain!\n```\n\n### Simbol didalam objek literal\n\nJika kita ingin menggunakan sebuah simbol dalam sebuah tulisan objek `{...}`, kita perlu menuliskan simbol tersebut dalam tanda kurung siku.\n\nSeperti ini:\n\n```js\nlet id = Symbol(\"id\");\n\nlet user = {\n  name: \"John\",\n*!*\n  [id]: 123 // bukan \"id\": 123\n*/!*\n};\n```\nItu karena kita memerlukan nilai dari variabel `id` sebagai kunci, bukan *string* dari \"id\".\n\n### Simbol diabaikan menggunakan *for..in*\n\nProperti-properti simbolis tidak ikut serta dalam pengulangan (*loop*) `for..in`.\n\nFor instance:\n\n```js run\nlet id = Symbol(\"id\");\nlet user = {\n  name: \"John\",\n  age: 30,\n  [id]: 123\n};\n\n*!*\nfor (let key in user) alert(key); // name, age (bukan simbol)\n*/!*\n\n// akses langsung dengan simbol, berfungsi\nalert( \"Direct: \" + user[id] );\n```\n\n`Object.keys(user)` juga mengabaikannya. Itu adalah bagian dari prinsip umum \"menyembunyikan properti simbolis\" (*hiding symbolic properties*). Jika *script* lain atau sebuah *library* melakukan pengulanagn pada objek kita, hal tersebut tidak akan mengakses sebuah properti simbolis tanpa diduga.\n\nSebaliknya, [Object.assign](mdn:js/Object/assign) menyalin baik *string* properti maupun simbol properti:\n\n```js run\nlet id = Symbol(\"id\");\nlet user = {\n  [id]: 123\n};\n\nlet clone = Object.assign({}, user);\n\nalert( clone[id] ); // 123\n```\n\nTidak ada paradoks di sini. Hal itu sesuai rancangan. Gagasan bahwa ketika kita meng-*clone* sebuah objek atau menyatukan (*merge*) objek-objek, kita biasanya ingin *semua* properti disalin (termasuk simbol seperti `id`).\n\n## Simbol global\n\nSeperti yang kita lihat, biasanya semua simbol itu berbeda, bahkan jika simbol-simbol tersebut memiliki nama yang sama. Tapi terkadang kita ingin simbol yang bernama sama untuk menjadi barang yang sama pula. Sebagai contoh, bagian-bagian lain dari aplikasi kita ingin mengakses simbol `\"id\"` yang berarti properti yang sama pula.\n\nUntuk melaksanakan aksi itu, hadirlah sebuah catatan simbol-simbol global (*global symbol registry*). Kita bisa membuat simbol-simbol di dalamnya dan mengaksesnya nanti, dan hal tersebut menjamin bahwa akses yang berulang oleh nama yang sama (akan) mengembalikan simbol yang sama pula.\n\nAgar bisa membaca (atau membuat jika belum ada) sebuah simbol dari catatan, gunakan `Symbol.for(key)`.\n\nHal itu memeriksa catatan (simbol) global, dan jika ada sebuah simbol yang dideskripsikan sebagai `key`, lalu mengembalikannya, jika tidak - buatlah sebuah simbol baru `Symbol(key)` dan menyimpan simbol baru tersebut dalam catatan sesuai namanya `key`.\n\nContohnya:\n\n```js run\n// membaca dari catatan global\nlet id = Symbol.for(\"id\"); // jika simbol tidak ada, simbol tersebut akan dibuat\n\n// baca simbol tersebut lagi (mungkin dari bagian lain dari kode)\nlet idAgain = Symbol.for(\"id\");\n\n// simbol yang sama\nalert( id === idAgain ); // true\n```\n\nSimbol-simbol di dalam catatan (*registry*) disebut sebagai *simbol-simbol global* (*global symbols*). Jika kita ingin sebuah simbol yang berlaku untuk keseluruhan aplikasi, dapat diakses dari mana saja dalam kode -- itulah kegunaan dari simbol global.\n\n```smart header=\"Simbol global itu seperti dalam Ruby\"\nDalam beberapa bahasa pemrograman, seperti Ruby, hanya ada satu simbol per nama.\n\nDalam JavaScript, seperti yang bisa kita lihat, yakni simbol-simbol global.\n```\n\n### Symbol.keyFor\n\nUntuk simbol-simbol global, tidak hanya `Symbol.for(key)` yang mengembalikan sebuah simbol berdasarkan nama, tetapi ada sebuah panggilan sebaliknya: `Symbol.keyFor(sym)`, sintaks tersebut melakukan hal sebaliknya tadi: mengembalikan sebuah nama berdasarkan sebuah simbol global.\n\nContohnya:\n\n```js run\n// mendapatkan simbol bersasarkan nama\nlet sym = Symbol.for(\"name\");\nlet sym2 = Symbol.for(\"id\");\n\n// mendapatkan nama berdasarkan simbol\nalert( Symbol.keyFor(sym) ); // name\nalert( Symbol.keyFor(sym2) ); // id\n```\n\n`Simbol.keyFor` secara internal menggunakan simbol registry global untuk mencari key/kunci dari simbolnya. Jadi itu tidak akan bekerja dengan simbol non-global. Jika simbolnya bukan global, itu tidak akan bisa menemukannya dan akan mengembalikan `undefined`.\n\nSeperti yang dikatakan, simbol apapun memiliki properti `description`.\n\nContohnya:\n\n```js run\nlet globalSymbol = Symbol.for(\"name\");\nlet localSymbol = Symbol(\"name\");\n\nalert( Symbol.keyFor(globalSymbol) ); // name, simbol global\nalert( Symbol.keyFor(localSymbol) ); // undefined, bukan simbol global\n\nalert( localSymbol.description ); // name\n```\n\n## Simbol-simbol sistem\n\nTerdapat banyak simbol-simbol \"sistem\" yang JavaScript gunakan secara internal, dan kita bisa menggunakan simbol-simbol sistem tersebut untuk mengatur dengan baik berbagai aspek dari objek kita.\n\nSimbol-simbol tersebut sudah terdaftar dalam spesifikasi di tabel [Simbol-simbol ternama (*well-known symbols*)](https://tc39.github.io/ecma262/#sec-well-known-symbols):\n\n- `Symbol.hasInstance`\n- `Symbol.isConcatSpreadable`\n- `Symbol.iterator`\n- `Symbol.toPrimitive`\n- ...dan seterusnya.\n\nContohnya, `Symbol.toPrimitive` membuat kita dapat mendeskripsikan objek menjadi hasil konversi yang primitif. Kita akan melihatnya segera.\n\nSimbol-simbol lain akan juga menjadi tidak asing ketika kita sedenang mempelajari fitur-fitur di bahasa pemrograman tersebut.\n\n## Ringkasan\n\n`Symbol` adalah sebuah jenis primitif dari pengindetifikasi yang unik.\n\nSimbol-simbol dibuat menggunakan panggilan `Symbol()` dengan sebuah deskripsi (nama).\n\nSimbols selalu berbeda nilainya, bahkan jika mereka memiliki nama yang sama. Jika kita ingin simbol-simbol yang bernama sama tersebut untuk menjadi (simbol yang) sama, maka kita harus menggunakan catatan (*registry*) global: `Symbol.for(key)` mengembalikan (membuat jika perlu) sebuah simbol global dengan  `key` yang sama dengan namanya. Panggilan-panggilan berganda pada `Symbol.for` dengan `key` yang sama mengenbalikan simbol yang persis sama.\n\nSimbol memiliki dua alasan utama pada pemakaiannya:\n\n1. Properti objek yang \"tersembunyi\".\n    Jika kita ingin menambahkan sebuah properti ke dala sebuah objek yang \"dimiliki\" oleh *script* lain atau sebuah *library*, kita bisa membuat sebuah simbol dan menggunakannya sebagai sebuah kunci properti. Sebuah properti simbolis tidak muncul dalam `for..in`, jadi hal tersbeut tidak akan tanpa sengaja terproses bersama properti-properti lain. Juga, simbol tidak akan diakses secara langsung, karena *script* tidak memiliki simbol kita. Jadi properti akan terlindungi dari penggunaan yang tak disengaja maupun tertimpa (*overwrite*).\n\n    Jadi kita bisa \"secara terselubung\" menyembunyikan sesuatu ke dalam objek yang kita inginkan, tetapi tidak bisa diliha oleh pihak lain, menggunakn properti simbolis.\n\n2. Terdapat banyak simbol sistem yang digunakan oleh oleh JavaScript yang mana dapat diakses sebagai `Symbol.*`. Kita bisa menggunakan simbol-simbol tersebut untuk mengubah beberapa perilaku bawaan (*built-in*). Sebagai contohnya, di tutorial selanjutnya kita akan menggunakan `Symbol.iterator` untuk [*iterables*](info:iterable), `Symbol.toPrimitive` untuk mengatur [konversi objek-ke-primitif](info:object-toprimitive) dan sebagainya.\n\nSecara teknis, simbol-simbol tidak 100% tersembunyi. Ada sebuah metode bawaan [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) yang membuat kita dapat mendapatkan semua simbol. Juga terdapat sebuah metode yang dinamakan [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) yang mengembalikan *semua* kunci dari sebuah objek termasuk yang kunci yang simbolik. Jadi simbol-simbol tersebut tidak sepenuhnya tersembunyi. Namun untuk sebagian besar *library*, fungsi-fungsi bawaan dan kontruksi sintaks constructs tidak menggunakan metode-metode ini.\n"
  },
  {
    "path": "1-js/04-object-basics/09-object-toprimitive/article.md",
    "content": "# Menolak konversi primitif\n\nApa yang terjadi ketika objek ditambahkan `obj1 + obj2`, dikurangi `obj1 - obj2` atau dicetak menggunakan `alert(obj)`?\n\nJavaScript tidak benar-benar memungkinkan untuk menyesuaikan cara operator bekerja pada objek. Tidak seperti beberapa bahasa pemrograman lain, seperti Ruby atau C++, kami tidak dapat mengimplementasikan metode objek khusus untuk menangani penambahan (atau operator lain).\n\nDalam kasus operasi seperti itu, objek secara otomatis dikonversi ke primitif, dan kemudian operasi dilakukan di atas primitif ini dan menghasilkan nilai primitif.\n\nItu batasan penting, karena hasil dari `obj1 + obj2` tidak bisa menjadi objek lain!\n\nMisalnya. kita tidak dapat membuat objek yang mewakili vektor atau matriks (atau pencapaian atau apa pun), menambahkannya dan mengharapkan objek \"dijumlahkan\" sebagai hasilnya. Prestasi arsitektur seperti itu secara otomatis \"di luar papan\".\n\nJadi, karena kita tidak bisa berbuat banyak di sini, tidak ada matematika dengan objek dalam proyek nyata. Ketika itu terjadi, biasanya karena kesalahan pengkodean.\n\nDalam bab ini kita akan membahas bagaimana sebuah objek dikonversi ke primitif dan bagaimana menyesuaikannya.\n\nKami memiliki dua tujuan:\n\n1. Ini akan memungkinkan kita untuk memahami apa yang terjadi jika terjadi kesalahan pengkodean, ketika operasi seperti itu terjadi secara tidak sengaja.\n2. Ada pengecualian, di mana operasi semacam itu dimungkinkan dan terlihat bagus. Misalnya. mengurangkan atau membandingkan tanggal (objek `Tanggal`). Kami akan menemukan mereka nanti.\n\n## Aturan konversi\n\nDalam bab <info:type-conversions> kita telah melihat aturan untuk konversi numerik, string, dan boolean dari primitif. Tapi kami meninggalkan celah untuk objek. Sekarang, seperti yang kita ketahui tentang metode dan simbol, menjadi mungkin untuk mengisinya.\n\n1. Semua objek `benar` dalam konteks boolean. Hanya ada konversi numerik dan string.\n2. Konversi numerik terjadi ketika kita mengurangi objek atau menerapkan fungsi matematika. Misalnya, objek `Tanggal` (akan dibahas dalam bab <info:tanggal>) dapat dikurangi, dan hasil dari `tanggal1 - tanggal2` adalah perbedaan waktu antara dua tanggal.\n3. Untuk konversi string -- biasanya terjadi ketika kita mengeluarkan objek seperti `alert(obj)` dan dalam konteks yang serupa.\n\nKita dapat menyempurnakan konversi string dan numerik, menggunakan metode objek khusus.\n\nAda tiga varian konversi tipe, yang terjadi dalam berbagai situasi.\n\nMereka disebut \"petunjuk\", seperti yang dijelaskan dalam [spesifikasi](https://tc39.github.io/ecma262/#sec-toprimitive):\n\n`\"tali\"`\n: Untuk konversi objek-ke-string, saat kita melakukan operasi pada objek yang mengharapkan string, seperti `alert`:\n\n    ```js\n    // keluaran\n    waspada (obj);\n\n    // menggunakan objek sebagai kunci properti\n    lainObj[obj] = 123;\n    ```\n\n`\"nomor\"`\n: Untuk konversi objek ke angka, seperti saat kita mengerjakan matematika:\n\n    ```js\n    // konversi eksplisit\n    misalkan angka = Angka(obj);\n\n    // matematika (kecuali biner plus)\n    misalkan n = +obj; // unary plus\n    biarkan delta = tanggal1 - tanggal2;\n\n    // lebih sedikit/perbandingan lebih besar\n    biarkan lebih besar = pengguna1 > pengguna2;\n    ```\n\n`\"default\"`\n: Terjadi dalam kasus yang jarang terjadi ketika operator \"tidak yakin\" jenis apa yang diharapkan.\n\n    Misalnya, biner plus `+` dapat bekerja baik dengan string (menggabungkannya) dan angka (menambahkannya), jadi string dan angka bisa digunakan. Jadi jika biner plus mendapatkan objek sebagai argumen, ia menggunakan petunjuk `\"default\"` untuk mengonversinya.\n\n    Selain itu, jika suatu objek dibandingkan menggunakan `==` dengan string, angka, atau simbol, konversi mana yang harus dilakukan juga tidak jelas, sehingga petunjuk `\"default\"` digunakan.\n\n    ```js\n    // binary plus menggunakan petunjuk \"default\"\n    misalkan total = obj1 + obj2;\n\n    // obj == nomor menggunakan petunjuk \"default\"\n    if (pengguna == 1) { ... };\n    ```\n\n    Operator perbandingan yang lebih besar dan lebih kecil, seperti `<` `>`, dapat bekerja dengan string dan angka juga. Namun, mereka menggunakan petunjuk `\"number\"`, bukan `\"default\"`. Itu karena alasan historis.\n\n    Namun dalam praktiknya, kita tidak perlu mengingat detail aneh ini, karena semua objek bawaan kecuali satu kasus (objek `Tanggal`, kita akan mempelajarinya nanti) mengimplementasikan konversi `\"default\"` dengan cara yang sama seperti ` \"nomor\"`. Dan kita bisa melakukan hal yang sama.\n\n```smart header=\"Tidak ada petunjuk `\\\"boolean\\\"`\"\nHarap dicatat -- hanya ada tiga petunjuk. Sesederhana itu.\n\nTidak ada petunjuk \"boolean\" (semua objek `benar` dalam konteks boolean) atau yang lainnya. Dan jika kita memperlakukan `\"default\"` dan `\"number\"` sama, seperti kebanyakan built-in, maka hanya ada dua konversi.\n```\n\n**Untuk melakukan konversi, JavaScript mencoba menemukan dan memanggil tiga metode objek:**\n\n1. Panggil `obj[Symbol.toPrimitive](hint)` - metode dengan kunci simbolis `Symbol.toPrimitive` (simbol sistem), jika metode tersebut ada,\n2. Sebaliknya jika petunjuknya adalah `\"string\"`\n    - coba `obj.toString()` dan `obj.valueOf()`, apa pun yang ada.\n3. Jika petunjuknya adalah `\"number\"` atau `\"default\"`\n    - coba `obj.valueOf()` dan `obj.toString()`, apa pun yang ada.\n\n## Symbol.toPrimitive\n\nMari kita mulai dari cara pertama. Ada simbol bawaan bernama `Symbol.toPrimitive` yang harus digunakan untuk menamai metode konversi, seperti ini:\n\n```js\nobj[Simbol.toPrimitif] = fungsi(petunjuk) {\n  // dia"
  },
  {
    "path": "1-js/04-object-basics/index.md",
    "content": "# Objects: dasar-dasar\n"
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/1-string-new-property/solution.md",
    "content": "\nCobalah jalankan:\n\n```js run\nlet str = \"Hello\";\n\nstr.test = 5; // (*)\n\nalert(str.test);\n```\n\nTergantung apakah kamu gunakan `use strict` atau tidak, hasilnya mungkin bisa:\n1. `undefined` (bukan strict mode)\n2. An error (strict mode).\n\nKenapa? Kita lihat apa yang terjadi apda baris `(*)`:\n\n1. Ketika properti dari `str` di akses, sebuah \"objek pembungkus\" dibuat.\n2. Didalam mode strict, menulis kedalamnya adalah sebuah error.\n3. Otherwise, the operation with the property is carried on, the object gets the `test` property, but after that the \"wrapper object\" disappears, so in the last line `str` has no trace of the property.\n3. Sebaliknya, operasi dengan propertinya dibawa, objeknya mendapatkan properti `test`, tapi setelah itu \"objek pembungkus\" menghilang, jadi di baris terakhir `str` tidak mempunyai jejak dari properti itu.\n\n**Contoh ini dengan jelas membuktikan bahwa primitif bukanlah sebuah objek**\n\nMereka tidak bisa menyimpan data tambahan."
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/1-string-new-property/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Bisakah saya menambahkan properti string?\n\n\nPerhatikan kode berikut:\n\n```js\nlet str = \"Hello\";\n\nstr.test = 5;\n\nalert(str.test);\n```\n\nBagaimana menurutmu, akankah itu bekerja? apa yang akan muncul?"
  },
  {
    "path": "1-js/05-data-types/01-primitives-methods/article.md",
    "content": "# Metode primitif\n\nJavascript memperbolehkan kita untuk bekerja dengan primitif (string, angka, dan lain-lain.) seperti jika mereka adalah objek. Mereka juga menyediakan metode untuk dipanggil. Kita akan belajar tentang hal itu nanti, tapi pertama kita akan melihat bagaimana hal itu bekerja, dan juga, primitif bukanlah objek (dan disini kita akan membuat hal itu lebih jelas).\n\nAyo kita lihat pada kunci perbedaan diantara primitif dan objek.\n\nPrimitif\n\n- Adalah sebuah nilai dari tipe primitif.\n- Ada 7 primitif tipe: `string`, `number`, `bigint`, `boolean`, `symbol`, `null` dan `undefined`.\n\nObjek\n\n- Mampu untuk menyimpan banyak nilai sebagai properti.\n- Bisa dibuat dengan menggunakan `{}`, contoh: `{name: \"John\", age: 30}`. Terdapat beberapa macam objek di Javascript: untuk contoh, fungsi, adalah objek.\n\nSalah satu hal yang terbaik tentang objek adalah kita bisa menyimpan fungsi sebagai salah satu dari propertinya.\n\n```js run\nlet john = {\n  name: \"John\",\n  sayHi: function() {\n    alert(\"Hi buddy!\");\n  }\n};\n\njohn.sayHi(); // Hi buddy!\n```\n\nJadi disini kita membuat sebuah objek `john` dengan method `sayHi`.\n\nAda juga built-in objek yang tersedia, seperti objek yang bekerja dengan tanggal, error, elemen HTML, dll. Mereka mempunyai properti dan method yang berbeda-beda.\n\nTapi, fitur ini ada dengan efek samping.\n\nObjek lebih \"berat\" dari primitif. Dan mereka membutuhkan sumber daya tambahan untuk mendukung pekerjaannya.\n\n## Sebuah primitif sebagai sebuah objek\n\nIni adalah paradoks yang dihadapi dari pencipta Javascript:\n\n- Terdapat banyak hal yang harus dilakukan dengan primitif seperti string atau angka. Akan menjadi lebih baik jika mereka bisa diakses sebagai method.\n- Primitives must be as fast and lightweight as possible.\n- Sebisa mungkin primitif haruslah cepat dan ringan.\n\nSolusinya terlihat sedikit aneh, tapi inilah solusinya:\n\n1. Primitif masih tetap primitif. Sebuah nilai tunggal, seperti yang diinginkan.\n2. Bahasanya membolehkan untuk mengakses method dan properti dari string, number, boolean dan symbols.\n3. Untuk membuat itu bekerja, \"objek pembungkus\" spesial yang menyediakan fungsionalitas tambahan dibuat, dan lalu dihancurkan.\n\n\"Objek pembungkus\" berbeda untuk setiap tipe primitif dan dipanggil: `String`, `Number`, `Boolean` dan `Symbol`. Lalu, mereka menyediakan metode-metode yang berbeda.\n\nContoh, ada methode string [str.toUpperCase()](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase) yang mengembalikan string `str` yang telah diubah menjadi huruf kapital.\n\nBeginilah caranya itu bekerja:\n\n```js run\nlet str = \"Hello\";\n\nalert( str.toUpperCase() ); // HELLO\n```\n\nSimpel, kan? Inilah sebenarnya yang terjadi didalam `str.toUpperCase()`:\n\n1. String `str` adalah sebuah primitif. Jadi sementara waktu untuk mengakses propertinya, sebuah objek spesial yang tahu tentang nilai dari string dibuat, dan memiliki metode yang berguna, seperti `toUpperCase()`.\n2. Metode itu menjalankan dan mengembalikan string baru (ditampilkan oleh `alert`).\n3. Objek spesial itu lalu dihancurkan, meninggalkan `str` primitif.\n\nJadi primitif bisa menyediakan metode, tapi mereka akan tetap ringan.\n\nMesin Javascript sangat mengoptimasi proses ini. Ini mungkin akan melewatkan pembuatan dari objek tambahan itu. Tapi itu masih melekat pada spesifikasinya dan bertingkah seperti jika itu memang diciptakan.\n\nSebuah angka mempunyai metodenya sendiri, contoh [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) membulatkan angka kedalam presisi yang diberikan:\n\n```js run\nlet n = 1.23456;\n\nalert( n.toFixed(2) ); // 1.23\n```\n\nKita akan melihat metode spesifik lainnya di bab <info:number> dan <info:string>.\n\n\n````warn header=\"Konstruktor `String/Number/Boolean` hanya digunakan untuk kebutuhan internal\" \nBeberapa bahasa seperti Java membolehkan kita untuk secara jelas membuat \"objek pembungkus\" untuk primitif menggunakan sintaks seperti `new Number(1)` atau `new Boolean(false)`.\n\nDidalam javascript, hal itu bisa dilakukan untuk beberapa alasan, tapi sangat **tidak direkomendasikan**. Beberapa hal akan menjadi rumit di beberapa tempat.\n\nContoh:\n\n```js run\nalert( typeof 0 ); // \"number\"\n\nalert( typeof new Number(0) ); // \"object\"!\n```\n\nObjek akan selalu truthy didalam `if`, jadi disini alert akan muncul:\n\n```js run\nlet zero = new Number(0);\n\nif (zero) { // zero adalah true, karena itu adalah sebuah objek\n  alert( \"zero is truthy!?!\" );\n}\n```\n\nDisisi lain, menggunakan fungsi yang sama `String/Number/Boolean` tanpa `new` adalah hal yang masuk akal dan hal yang berguna. Mereka mengubah nilai kedalam tipe yang sesuai: kedalam sebuah string, sebuah number, atau sebuah boolean(primitif).\n\nContoh, hal ini sepenuhnya valid:\n```js\nlet num = Number(\"123\"); // mengubah string menjadi angka\n```\n````\n\n\n````warn header=\"null/undefined tidak memiliki method\"\nPrimitif spesial `null` dan `undefined` adalah pengecualian. Mereka tidak mempunyai \"objek pembungkus\" yang sesuai dan tidak menyediakan metode. Dalam arti tertentu, mereka adalah \"yang paling primitif\".\n\nPercobaan untuk mengakses properti seperti nilai akan memberikan error:\n\n```js run\nalert(null.test); // error\n````\n\n## Ringkasan\n\n- Primitif kecuali `null` dan `undefined` menyediakan banyak metode yang berguna. Kita akan belajar hal itu di bab selanjutnya.\n- Secara formal, metode-metode ini akan bekerja dengan menggunakan objek sementara, tapi mesin Javascript telah dibuat dengan baik untuk mengoptimasi hal itu secara internal, jadi mereka tidaklah sulit untuk dipanggil.\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/1-sum-interface/solution.md",
    "content": "\n\n```js run demo\nlet a = +prompt(\"The first number?\", \"\");\nlet b = +prompt(\"The second number?\", \"\");\n\nalert( a + b );\n```\n\nCatatan bahwa unary plus `+` sebelum `prompt`. Segera mengkonversi nilai ke angka.\n\nJika tidak, `a` dan `b` akan menjadi string jumlah mereka akan menjadi gabungan mereka, yaitu: `\"1\" + \"2\" = \"12\"`."
  },
  {
    "path": "1-js/05-data-types/02-number/1-sum-interface/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Jumlahkan angka dari pengunjung\n\nBuat skrip yang meminta pengunjung untuk memasukkan dua angka dan kemudian menunjukkan jumlah mereka.\n\n[demo]\n\nN.B. Ada gotcha dengan tipe.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/2-why-rounded-down/solution.md",
    "content": "Secara internal pecahan desimal `6.35` adalah sebuah biner tanpa akhir. Seperti biasa dalam kasus seperti ini, disimpan dengan kehilangan presisi.\n\nAyo lihat:\n\n```js run\nalert( 6.35.toFixed(20) ); // 6.34999999999999964473\n```\n\nKehilangan presisi dapat menyebabkan peningkatan dan penurunan angka. Dalam kasus khusus ini jumlahnya menjadi sedikit lebih sedikit, itu sebabnya dibulatkan.\n\nDan apa untuk `1.35`?\n\n```js run\nalert( 1.35.toFixed(20) ); // 1.35000000000000008882\n```\n\nDi sini kehilangan presisi membuat jumlahnya sedikit lebih besar, jadi itu dibulatkan.\n\n**Bagaimana kita dapat memperbaiki masalah dengan `6.35` jika kita ingin itu dibulatkan dengan cara yang benar?**\n\nKita harus membawanya lebih dekat ke integer sebelum pembulatan:\n\n```js run\nalert( (6.35 * 10).toFixed(20) ); // 63.50000000000000000000\n```\n\nPerhatikan bahwa `63.5` tidak memiliki kehilangan presisi sama sekali. Itu karena bagian desimal `0,5` sebenarnya` 1 / 2`. Pecahan yang dibagi oleh kekuatan `2` persis diwakili dalam sistem biner, sekarang kita dapat membulatkannya:\n\n\n```js run\nalert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(rounded) -> 6.4\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/2-why-rounded-down/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Kenapa 6.35.toFixed(1) == 6.3?\n\nBerdasarkan dokumentasi `Math.round` dan `toFixed` keduanya membulatkan ke angka terdekat: `0..4` turun sementara `5..9` naik.\n\nContohnya:\n\n```js run\nalert( 1.35.toFixed(1) ); // 1.4\n```\n\nDalam contoh serupa di bawah ini, mengapa `6.35` dibulatkan menjadi `6.3`, dan tidak `6.4`?\n\n```js run\nalert( 6.35.toFixed(1) ); // 6.3\n```\n\nBagaimana untuk membulatkan `6.35` dengan benar?\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/_js.view/solution.js",
    "content": "\nfunction readNumber() {\n  let num;\n\n  do {\n    num = prompt(\"Enter a number please?\", 0);\n  } while ( !isFinite(num) );\n\n  if (num === null || num === '') return null;\n  \n  return +num;\n}"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/_js.view/test.js",
    "content": "beforeEach(function() {\n  sinon.stub(window, \"prompt\");\n});\n\nafterEach(function() {\n  prompt.restore();\n});\n\ndescribe(\"readNumber\", function() {\n\n  it(\"if a number, returns it\", function() {\n    prompt.returns(\"123\");\n    assert.strictEqual(readNumber(), 123);\n  });\n\n  it(\"if 0, returns it\", function() {\n    prompt.returns(\"0\");\n    assert.strictEqual(readNumber(), 0);\n  });\n\n  it(\"continues the loop until meets a number\", function() {\n    prompt.onCall(0).returns(\"not a number\");\n    prompt.onCall(1).returns(\"not a number again\");\n    prompt.onCall(2).returns(\"1\");\n    assert.strictEqual(readNumber(), 1);\n  });\n\n  it(\"if an empty line, returns null\", function() {\n    prompt.returns(\"\");\n    assert.isNull(readNumber());\n  });\n\n  it(\"if cancel, returns null\", function() {\n    prompt.returns(null);\n    assert.isNull(readNumber());\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/solution.md",
    "content": "\n```js run demo\nfunction readNumber() {\n  let num;\n\n  do {\n    num = prompt(\"Enter a number please?\", 0);\n  } while ( !isFinite(num) );\n\n  if (num === null || num === '') return null;\n  \n  return +num;\n}\n\nalert(`Read: ${readNumber()}`);\n```\n\nSolusinya sedikit lebih rumit dari itu karena kita perlu menangani `null`/baris kosong.\n\nJadi, kita benar-benar menerima input hingga ini merupakan \"angka reguler\". Baik `null` (cancel) maupun baris kosong juga cocok dengan kondisi itu, karena dalam bentuk numerik mereka adalah` 0`.\n\nSetelah kita berhenti, kita perlu memperlakukan `null` dan khususnya baris kosong (mengembalikan `null`), karena mengonversinya menjadi angka akan mengembalikan `0`.\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/3-repeat-until-number/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Ulangi sampai masukan adalah sebuah angka\n\nBuatlah sebuah fungsi `readNumber` yang meminta (prompts) nomor hingga pengunjung memasukkan nilai numerik yang valid.\n\nNilai yang dihasilkan harus dikembalikan sebagai angka.\n\nPengunjung juga dapat menghentikan proses dengan memasukkan baris kosong atau menekan \"BATAL\". Dalam hal ini, fungsi tersebut harus mengembalikan `null`.\n\n[demo]\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/4-endless-loop-error/solution.md",
    "content": "Itu karena `i` tidak akan pernah sebanding dengan `10`.\n\nJalankan ini untuk melihat nilai *real* dari `i`:\n\n```js run\nlet i = 0;\nwhile (i < 11) {\n  i += 0.2;\n  if (i > 9.8 && i < 10.2) alert( i );\n}\n```\n\nTidak satu pun dari mereka yang benar-benar `10`.\n\nHal-hal seperti itu terjadi karena kehilangan presisi ketika menambahkan pecahan seperti `0,2`.\n\nKesimpulan: menghindari pemeriksaan kesetaraan saat bekerja dengan pecahan desimal."
  },
  {
    "path": "1-js/05-data-types/02-number/4-endless-loop-error/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Lingkaran tak terbatas tak berkala\n\nLoop ini tidak terbatas. Tidak pernah berakhir. Mengapa?\n\n```js\nlet i = 0;\nwhile (i != 10) {\n  i += 0.2;\n}\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/8-random-min-max/solution.md",
    "content": "Kita perlu \"memetakan\" semua nilai dari interval 0..1 ke dalam nilai dari `min` ke` max`.\n\nItu bisa dilakukan dalam dua tahap:\n\n1. Jika kita mengalikan angka acak dari 0..1 dengan `max-min`, maka interval nilai yang mungkin meningkat` 0..1` ke `0..max-min`.\n2. Sekarang jika kita menambahkan `min`, interval yang mungkin menjadi dari` min` ke `max`.\n\nFungsi:\n\n```js run\nfunction random(min, max) {\n  return min + Math.random() * (max - min);\n}\n\nalert( random(1, 5) ); \nalert( random(1, 5) ); \nalert( random(1, 5) ); \n```\n\n"
  },
  {
    "path": "1-js/05-data-types/02-number/8-random-min-max/task.md",
    "content": "nilai penting: 2\n\n---\n\n# Sebuah angka acak dari min ke max\n\nFungsi bawaan `Math.random()` membuat sebuah angka acak dari `0` ke `1` (tidak termasuk `1`).\n\nTulis fungsi `random(min, max)` untuk menghasilkan angka floating-point acak dari `min` ke` max` (tidak termasuk `max`).\n\nContoh kerjanya:\n\n```js\nalert( random(1, 5) ); // 1.2345623452\nalert( random(1, 5) ); // 3.7894332423\nalert( random(1, 5) ); // 4.3435234525\n```\n"
  },
  {
    "path": "1-js/05-data-types/02-number/9-random-int-min-max/solution.md",
    "content": "# The simple but wrong solution\n\nThe simplest, but wrong solution would be to generate a value from `min` to `max` and round it:\n\n```js run\nfunction randomInteger(min, max) {\n  let rand = min + Math.random() * (max - min); \n  return Math.round(rand);\n}\n\nalert( randomInteger(1, 3) );\n```\n\nThe function works, but it is incorrect. The probability to get edge values `min` and `max` is two times less than any other.\n\nIf you run the example above many times, you would easily see that `2` appears the most often.\n\nThat happens because `Math.round()` gets random numbers from the interval `1..3` and rounds them as follows:\n\n```js no-beautify\nvalues from 1    ... to 1.4999999999  become 1\nvalues from 1.5  ... to 2.4999999999  become 2\nvalues from 2.5  ... to 2.9999999999  become 3\n```\n\nNow we can clearly see that `1` gets twice less values than `2`. And the same with `3`.\n\n# The correct solution\n\nThere are many correct solutions to the task. One of them is to adjust interval borders. To ensure the same intervals, we can generate values from `0.5 to 3.5`, thus adding the required probabilities to the edges:\n\n```js run\n*!*\nfunction randomInteger(min, max) {\n  // now rand is from  (min-0.5) to (max+0.5)\n  let rand = min - 0.5 + Math.random() * (max - min + 1);\n  return Math.round(rand);\n}\n*/!*\n\nalert( randomInteger(1, 3) );\n```\n\nAn alternative way could be to use `Math.floor` for a random number from `min` to `max+1`:\n\n```js run\n*!*\nfunction randomInteger(min, max) {\n  // here rand is from min to (max+1)\n  let rand = min + Math.random() * (max + 1 - min);\n  return Math.floor(rand);\n}\n*/!*\n\nalert( randomInteger(1, 3) );\n```\n\nNow all intervals are mapped this way:\n\n```js no-beautify\nvalues from 1  ... to 1.9999999999  become 1\nvalues from 2  ... to 2.9999999999  become 2\nvalues from 3  ... to 3.9999999999  become 3\n```\n\nAll intervals have the same length, making the final distribution uniform.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/9-random-int-min-max/task.md",
    "content": "nilai penting: 2\n\n---\n\n# Sebuah integer acak dari min ke max\n\nBuatlah sebuah fungsi `randomInteger(min, max)` yang menghasilkan angka *integer* acak dari `min` ke `max` termasuk keduanya `min` dan `max` sebagai nilai yang mungkin.\n\nAngka apa pun dari interval `min..max` harus muncul dengan probabilitas yang sama.\n\n\nContoh kerjanya:\n\n```js\nalert( randomInteger(1, 5) ); // 1\nalert( randomInteger(1, 5) ); // 3\nalert( randomInteger(1, 5) ); // 5\n```\n\nAnda dapat menggunakan solusi dari [tugas sebelumnya](info:task/random-min-max) sebagai basis.\n"
  },
  {
    "path": "1-js/05-data-types/02-number/article.md",
    "content": "# Angka\n\nDalam JavaScript modern, ada dua tipe angka:\n\n1. Angka regular di JavaScript yang disimpan dalam format 64-bit [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), juga dikenal sebagai \"angka double precision floating point\". Inilah angka yang kita paling sering kita pakai, dan kita akan bahas tentang mereka di bab ini.\n\n2. Angka BigInt, untuk mewakili integer dengan panjang sembarang. Mereka kadang dibutuhkan, karena angka regular tak bisa lebih dari <code>2<sup>53</sup></code> atau kurang dari <code>-2<sup>53</sup></code>. Karena bigint dipakai di sedikit area spesial, kita khususkan mereka bab spesial <info:bigint>.\n\nJadi di sini kita akan bahas angka regular. Ayo perluas pengetahuan kita tentang mereka.\n\n## Cara lain menulis angka\n\nBayangkan kita harus menulis 1 milyar. Cara jelasnya begini:\n\n```js\nlet billion = 1000000000;\n```\n\nKita juga bisa menggunakan `_` sebagai pemisahnya:\n\n```js\nlet billion = 1_000_000_000;\n```\n\nDi sini, garis bawah `_` memainkan peran sebagai \"syntactic sugar\", ini membuat angka lebih mudah dibaca. Mesin JavaScript mengabaikan `_` di antara digit, jadi nilainya sama persis dengan satu miliar di atas.\n\nTapi di kehidupan nyata, kita biasanya menghindari menulis string nol yang panjang karena rentan terjadi kesalahan. Selain itu, kita malas. Kita biasanya akan menulis sesuatu seperti `\"1bn\"` untuk milyar atau `\"7.3bn\"` untuk 7 milyar 300 juta. Sama halnya dengan angka besar lainnya.\n\nDi JavaScript, kita perpendek angka dengan menambah huruf `\"e\"` ke angka dan menspesifikasi jumlah nol:\n\n```js run\nlet billion = 1e9;  // 1 milyar, literalnya: 1 dan 9 nol\n\nalert( 7.3e9 );  // 7.3 milyar (7,300,000,000)\n```\n\nDengan kata lain, `\"e\"` kalikan angkanya dengan `1` dengan jumlah nol yang diberikan.\n\n```js\n1e3 = 1 * 1000 // e3 means *1000\n1.23e6 = 1.23 * 1000000 // e6 means *1000000\n```\n\nSekarang ayo tulis sesuatu lebih kecil. Katakan, 1 microsecond (sepersejuta second):\n\n```js\nlet ms = 0.000001;\n```\n\nSama seperti sebelumnya, memakai `\"e\"` bisa membantu. Jika kita ingin menghindari menulis nol eksplisit, kita bisa katakan hal yang sama:\n\n```js\nlet ms = 1e-6; // enam nol di sebelah kiri dari 1\n```\n\nJika kita hitung nol di `0.000001`, ada 6 dari mereka. Jadi alaminya `1e-6`.  \n\nDengan kata lain, angka negatif setelah `\"e\"` artinya pembagian 1 dengan jumlah nol yang diberikan:\n\n```js\n// -3 membagi 1 dengan 3 nol\n1e-3 = 1 / 1000 (=0.001)\n\n// -6 membagi 1 dengan 6 nol\n1.23e-6 = 1.23 / 1000000 (=0.00000123)\n```\n\n### Hex, angka binary dan octal\n\nAngka [Hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal) secara luas dipakai di JavaScript untuk mewakili warna, encode karakter, dan banyak hal lain. Alaminya, ada cara lebih singkat menulis mereka: `0x` kemudian angkanya.\n\nMisalnya:\n\n```js run\nalert( 0xff ); // 255\nalert( 0xFF ); // 255 (sama, case diabaikan)\n```\n\nSistem numeral binary dan octal jarang dipakai, tapi juga didukung menggunakan prefix `0b` dan `0o`:\n\n\n```js run\nlet a = 0b11111111; // bentuk binary dari 255\nlet b = 0o377; // bentuk octal dari 255\n\nalert( a == b ); // true, angka sama 255 dari kedua sisi\n```\n\nCuma ada 3 sistem numeral dengan dukungan begitu. Untuk sistem numeral lain, kita sebaiknya memakai fungsi `parseInt` (yang akan kita lihat nanti di bab ini).\n\n## toString(base)\n\nMetode `num.toString(base)` mengembalikan representasi string dari `num` di sistem numeral dengan `base` yang diberikan.\n\nMisalnya:\n```js run\nlet num = 255;\n\nalert( num.toString(16) );  // ff\nalert( num.toString(2) );   // 11111111\n```\n\n`base` bisa bervariasi dari `2` hingga `36`. Defaultnya `10`.\n\nPenggunaan umumnya ialah:\n\n- **base=16** dipakai untuk warna hex, character encoding dll, digit bisa `0..9` atau `A..F`.\n- **base=2** paling banyak untuk mendebug operasi bitwise, digit bisa `0` atau `1`.\n- **base=36** ini maximum, digit bisa `0..9` atau `A..Z`. Seluruh alfabet latin dipakai untuk merepresentasi angka. Hal lucu, tapi berguna untuk `36` ialah saat kita harus mengubah identifier numerik panjang ke dalam sesuatu yang lebih pendek, misalnya untuk membuat url pendek. Bisa direpresentasikan dalam sistem `36`:\n\n    ```js run\n    alert( 123456..toString(36) ); // 2n9c\n    ```\n\n```warn header=\"Dua dot untuk memanggil metode\"\nTolong ingat bahwa dua dot di `123456..toString(36)` bukan typo. Jika kita mau memanggil langsung metode pada angka, seperti `toString` di contoh di atas, maka kita harus menaruh dua dot `..` setelahnya.\n\nJika kita menaruh dot tunggal: `123456.toString(36)`, maka akan ada galat, karena syntax JavaScript berimplikasi bahwa bagian desimal setelah dot pertama. Dan jika kita menaruh satu dot lagi, maka JavaScript tahu bahwa bagian desimal kosong dan sekarang pergi ke metode.\n\nJuga bisa menulis `(123456).toString(36)`.\n```\n\n## Pembulatan\n\nSatu dari operasi paling banyak dipakai saat bekerja dengan angka ialah pembulatan.\n\nAda beberapa fungsi built-in untuk pembulatan:\n\n`Math.floor`\n: Membulat ke bawah: `3.1` menjadi `3`, dan `-1.1` menjadi `-2`.\n\n`Math.ceil`\n: Membulat ke atas: `3.1` menjadi `4`, dan `-1.1` menjadi `-1`.\n\n`Math.round`\n: Membulatkan ke bilangan bulat terdekat: `3.1` menjadi` 3`, `3.6` menjadi` 4`, huruf tengah: `3.5` juga dibulatkan ke atas menjadi` 4`.\n\n`Math.trunc` (tidak didukung oleh Internet Explorer)\n: Menghapus apa pun setelah koma desimal tanpa pembulatan: `3.1` menjadi` 3`, `-1.1` menjadi` -1`.\n\nIni tabel untuk meringkas perbedaan di antara mereka:\n\n|   | `Math.floor` | `Math.ceil` | `Math.round` | `Math.trunc` |\n|---|---------|--------|---------|---------|\n|`3.1`|  `3`    |   `4`  |    `3`  |   `3`   |\n|`3.6`|  `3`    |   `4`  |    `4`  |   `3`   |\n|`-1.1`|  `-2`    |   `-1`  |    `-1`  |   `-1`   |\n|`-1.6`|  `-2`    |   `-1`  |    `-2`  |   `-1`   |\n\n\nFungsi ini membahas semua cara yang mungkin untuk berhadapan dengan bagian desimal dari angka. Tapi bagaimana jika kita mau membulatkan angka ke digit `ke-n` setelah desimal?\n\nMisalnya, kita punya `1.2345` dan mau membulatkan ke 2 digit, memperoleh `1.23`.\n\nAda dua cara melakukannya:\n\n1. Kali-dan-bagi.\n\n    Misalnya, untuk membulatkan angka ke digit kedua setelah desimal, kita bisa mengalikan angkanya dengan `100` (atau sebuah pangkat dari 10), panggil fungsi pembulatan lalu membagi itu kembali.\n    ```js run\n    let num = 1.23456;\n\n    alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23\n    ```\n\n2. Metode [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed) Membulatkan ke digit `n` setelah poin itu dan mengembalikan representasi string dari hasilnya.\n\n    ```js run\n    let num = 12.34;\n    alert( num.toFixed(1) ); // \"12.3\"\n    ```\n\n    Ini membulatkan ke atas atau ke bawah ke nilai terdekat, serupa dengan `Math.round`:\n\n    ```js run\n    let num = 12.36;\n    alert( num.toFixed(1) ); // \"12.4\"\n    ```\n\n    Silakan catat hasil dari `toFixed` ialah string. Jika bagian desimal lebih pendek dari yang dibutuhkan, nol ditambahkan di akhir:\n\n    ```js run\n    let num = 12.34;\n    alert( num.toFixed(5) ); // \"12.34000\", tambah nol supaya tepat 5 digit\n    ```\n\n    Kita bisa mengkonversi itu ke angka menggunakan unary plus atau panggilan `Number()`: `+num.toFixed(5)`.\n\n## Kalkulasi yang tidak tepat\n\nSecara internal, angka direpresentasikan dalam format 64-bit [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754-2008_revision), jadi ada tepat 64 bit untuk menyimpan angka: 52 di antaranya digunakan untuk menyimpan angka, 11 di antaranya menyimpan posisi titik desimal (nol untuk angka bilangan bulat), dan 1 bit untuk tanda.\n\nJika angka terlalu besar, itu akan meluapkan penyimpanan 64-bit, berpotensi memberikan infinity:\n\n```js run\nalert( 1e500 ); // Infinity\n```\n\nYang mungkin agak kurang jelas, tetapi sering terjadi adalah hilangnya ketepatan.\n\nPertimbangkan (falsy!) tes ini:\n\n```js run\nalert( 0.1 + 0.2 == 0.3 ); // *!*false*/!*\n```\n\nBenar, jika kita memeriksa jumlah dari `0.1` dan `0.2` adalah `0.3`, kita mendapatkan `false`.\n\nAneh! Kenapa hasilnya itu dan tidak `0.3`?\n\n```js run\nalert( 0.1 + 0.2 ); // 0.30000000000000004\n```\n\nAduh! Ada lebih banyak konsekuensi daripada perbandingan yang salah di sini. Bayangkan Anda membuat situs e-shopping dan pengunjung memasukkan barang-barang `$ 0,10` dan` $ 0,20` ke troli mereka. Total pesanan akan `$ 0,30000000000000004`. Itu akan mengejutkan siapa pun.\n\nTetapi kenapa hal ini bisa terjadi?\n\nSebuah angka disimpan di memori dalam bentuk binary, sebuah urutan dari bits - satu dan nol. Tetapi bilangan pecahan seperti `0.1`, `0.2` yang terlihat sederhana dalam sistem angka desimal sebenarnya adalah pecahan tak berujung dalam bentuk binernya.\n\nDengan kata lain, apa itu `0,1`? Ini adalah satu dibagi dengan sepuluh `1 / 10`, sepersepuluh. Dalam sistem angka desimal, angka-angka seperti itu mudah diwakili. Bandingkan dengan sepertiga: `1 / 3`. Ini menjadi pecahan yang tak berujung `0,33333 (3)`.\n\nJadi, pembagian dengan kekuatan `10` dijamin bekerja dengan baik dalam sistem desimal, tetapi pembagian dengan `3` tidak. Untuk alasan yang sama, dalam sistem angka biner, pembagian dengan kekuatan `2` dijamin bekerja, tetapi `1 / 10` menjadi fraksi biner tanpa akhir.\n\nTidak ada cara untuk menyimpan * tepat 0,1 * atau * persis 0,2 * menggunakan sistem biner, sama seperti tidak ada cara untuk menyimpan sepertiga sebagai fraksi desimal.\n\nFormat numerik IEEE-754 memecahkan ini dengan membulatkan ke angka terdekat yang mungkin. Aturan pembulatan ini biasanya tidak memungkinkan kita untuk melihat bahwa \"kehilangan presisi kecil\", tetapi itu ada.\n\nKita bisa melihat ini dalam aksi:\n```js run\nalert( 0.1.toFixed(20) ); // 0.10000000000000000555\n```\n\nDan ketika kita menjumlahkan dua angka, \"kehilangan presisi\" mereka bertambah.\n\nItu sebabnya `0,1 + 0,2` tidak sepenuhnya` 0,3`.\n\n```smart header=\"Tidak hanya JavaScript\"\nMasalah yang sama ada di banyak bahasa pemrograman lainnya.\n\nPHP, Java, C, Perl, Ruby memberikan hasil yang sama persis, karena mereka didasarkan pada format numerik yang sama.\n```\n\nBisakah kita mengatasi masalah ini? Tentu, metode yang paling dapat diandalkan adalah melengkapi hasilnya dengan bantuan metode [toFixed(n)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed):\n\n```js run\nlet sum = 0.1 + 0.2;\nalert( sum.toFixed(2) ); // 0.30\n```\n\nHarap dicatat bahwa `toFixed` selalu mengembalikan string. Ini memastikan bahwa ia memiliki 2 digit setelah titik desimal. Itu sebenarnya nyaman jika kita memiliki e-shopping dan perlu menunjukkan `$ 0,30`. Untuk kasus lain, kita dapat menggunakan plus unary untuk memaksanya menjadi nomor:\n\n```js run\nlet sum = 0.1 + 0.2;\nalert( +sum.toFixed(2) ); // 0.3\n```\n\nKita juga dapat sementara mengalikan angka dengan 100 (atau angka yang lebih besar) untuk mengubahnya menjadi bilangan bulat, menghitung, dan kemudian membaginya kembali. Kemudian, saat kita mengerjakan matematika dengan bilangan bulat, kesalahannya agak berkurang, tetapi kita masih mendapatkannya di pembagian:\n\n```js run\nalert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3\nalert( (0.28 * 100 + 0.14 * 100) / 100); // 0.4200000000000001\n```\n\nJadi, pendekatan perkalian/pembagian mengurangi kesalahan, tetapi tidak menghapusnya sama sekali.\n\nTerkadang kita bisa mencoba menghindari pecahan sama sekali. Seperti jika kita berurusan dengan toko, maka kita dapat menyimpan harga dalam sen, bukan dalam dolar. Tetapi bagaimana jika kita menerapkan diskon 30%? Dalam praktiknya, menghindari pecahan sama sekali jarang dimungkinkan. Hanya bulatkan mereka untuk memotong \"ekor\" bila diperlukan.\n\n````smart header=\"Lucunya\"\nCoba jalankan ini:\n\n```js run\n// Halo! saya adalah angka yang meningkat sendiri!\nalert( 9999999999999999 ); // menunjukkan 10000000000000000\n```\n\nPenderitaan ini berasal dari masalah yang sama: kehilangan presisi. Ada 64 bit untuk angka, 52 di antaranya dapat digunakan untuk menyimpan digit, tetapi itu tidak cukup. Jadi digit yang paling tidak penting menghilang.\n\nJavaScript tidak memicu kesalahan dalam kejadian semacam itu. Itu melakukan yang terbaik untuk memasukkan angka ke dalam format yang diinginkan, tetapi sayangnya, format ini tidak cukup besar.\n````\n\n```smart header=\"Dua nol\"\nKonsekuensi lucu yang lain dari representasi internal dari angka adalah adanya dua nol: `0` dan` -0`.\n\nItu karena sebuah tanda diwakili oleh satu bit, sehingga dapat diatur atau tidak diatur untuk angka apa pun termasuk nol.\n\nDalam kebanyakan kasus perbedaannya tidak terlalu mencolok, karena operator cocok untuk memperlakukan mereka sebagai sesuatu yang sama.\n```\n\n## Tests: isFinite dan isNaN\n\nIngat dua nilai numerik khusus ini?\n\n- `Infinity` (dan `-Infinity`) adalah nilai numerik khusus yang lebih besar (kurang) dari apa pun.\n- `NaN` mewakili kesalahan.\n\nMereka termasuk dalam tipe `angka`, tetapi bukan angka \"normal\", jadi ada fungsi khusus untuk memeriksanya:\n\n\n- `isNaN(value)` mengubah argumennya menjadi angka dan kemudian mengujinya `NaN`:\n\n    ```js run\n    alert( isNaN(NaN) ); // true\n    alert( isNaN(\"str\") ); // true\n    ```\n\n    Tetapi apakah kita membutuhkan fungsi ini? Tidak bisakah kita menggunakan perbandingan `=== NaN`? Maaf, tapi jawabannya adalah tidak. Nilai `NaN` unik karena tidak sama dengan apa pun, termasuk dirinya sendiri:\n\n    ```js run\n    alert( NaN === NaN ); // false\n    ```\n\n- `isFinite(value)` mengonversi argumennya menjadi angka dan mengembalikan `true` jika itu angka biasa, bukan `NaN/Infinity/-Infinity`:\n\n    ```js run\n    alert( isFinite(\"15\") ); // true\n    alert( isFinite(\"str\") ); // false, karena nilai khusus: NaN\n    alert( isFinite(Infinity) ); // false, karena nilai khusus: Infinity\n    ```\n\nTerkadang `isFinite` digunakan untuk memvalidasi apakah sebuah nilai string adalah sebuah angka reguler:\n\n\n```js run\nlet num = +prompt(\"Enter a number\", '');\n\n// akan benar kecuali jika Anda memasukkan Infinity, -Infinity atau bukan angka\nalert( isFinite(num) );\n```\n\nHarap dicatat bahwa string kosong atau spasi-saja diperlakukan sebagai `0` dalam semua fungsi numerik termasuk` isFinite`.\n\n```smart header=\"Dibandingkan dengan `Object.is`\"\n\nAda metode bawaan khusus [Object.is](mdn:js/Object/is) yang membandingkan nilai seperti `===`, tetapi lebih dapat diandalkan untuk dua kasus:\n\n1. Ini bekerja dengan `NaN`: `Object.is(NaN, NaN) === true`, itu hal yang bagus.\n2. Nilai `0` and `-0` adalah berbeda: `Object.is(0, -0) === false`, secara teknis adalah benar, karena secara internal nomor tersebut memiliki bit tanda yang mungkin berbeda bahkan jika semua bit lainnya nol.\n\nPada kasus lain, `Object.is(a, b)` adalah sama dengan `a === b`.\n\nCara perbandingan ini sering digunakan dalam spesifikasi JavaScript. Ketika suatu algoritma internal perlu membandingkan dua nilai untuk menjadi persis sama, ia menggunakan `Object.is` (secara internal disebut [SameValue](https://tc39.github.io/ecma262/#sec-samevalue)).\n```\n\n\n## parseInt dan parseFloat\n\nKonversi angka menggunakan nilai tambah `+` atau `Number()` sangat ketat. Jika suatu nilai bukan angka, itu gagal:\n\n```js run\nalert( +\"100px\" ); // NaN\n```\n\nSatu-satunya pengecualian adalah spasi di awal atau di akhir string, karena diabaikan.\n\nTetapi dalam kehidupan nyata kita sering memiliki nilai dalam satuan, seperti `\"100px\"` atau `\"12pt\"` dalam CSS. Juga di banyak negara simbol mata uang mengikuti jumlah, jadi kita memiliki `\"€19\"` dan ingin mengekstraksi nilai numerik dari itu.\n\nUntuk itulah `parseInt` dan` parseFloat` ada.\n\nMereka \"membaca\" angka dari sebuah string sampai mereka tidak bisa. Jika terjadi kesalahan, nomor yang dikumpulkan dikembalikan. Fungsi `parseInt` mengembalikan integer, sementara` parseFloat` akan mengembalikan nomor floating-point:\n\n```js run\nalert( parseInt('100px') ); // 100\nalert( parseFloat('12.5em') ); // 12.5\n\nalert( parseInt('12.3') ); // 12, hanya bagian integer yang dikembalikan\nalert( parseFloat('12.3.4') ); // 12.3, poin kedua berhenti membaca\n```\n\nAda situasi dimana `parseInt/parseFloat` akan mengembalikan `NaN`. Ini terjadi ketika tidak ada digit yang bisa dibaca\n\n```js run\nalert( parseInt('a123') ); // NaN, symbol pertama menghentikan proses\n```\n\n````smart header=\"Pernyataan kedua dari `parseInt(str, radix)`\"\nFungsi `parseInt()` fungsi memiliki parameter opsional kedua. Ini menentukan dasar dari sistem angka, jadi `parseInt` juga dapat mengurai string nomor hex, angka biner dan sebagainya:\n\n```js run\nalert( parseInt('0xff', 16) ); // 255\nalert( parseInt('ff', 16) ); // 255, tanpa 0x juga bekerja\n\nalert( parseInt('2n9c', 36) ); // 123456\n```\n````\n\n## Fungsi matematika lainnya\n\nJavascript memiliki objek [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) bawaan dimana berisi perpustakaan kecil fungsi matematika dan konstanta.\n\nBeberapa contoh:\n\n`Math.random()`\n: Mengembalikan angka acak dari 0 hingga 1 (tidak termasuk 1)\n\n    ```js run\n    alert( Math.random() ); // 0.1234567894322s\n    alert( Math.random() ); // 0.5435252343232\n    alert( Math.random() ); // ... (angka aca apa saja)\n    ```\n\n`Math.max(a, b, c...)` / `Math.min(a, b, c...)`\n: Mengembalikan argumen terbesar / terkecil dari jumlah argumen yang arbitrer.\n\n    ```js run\n    alert( Math.max(3, 5, -10, 0, 1) ); // 5\n    alert( Math.min(1, 2) ); // 1\n    ```\n\n`Math.pow(n, power)`\n: mengembalikan bilangan `n` yang dipangkatkan.\n\n    ```js run\n    alert( Math.pow(2, 10) ); // 2 pangkat 10 = 1024\n    ```\n\nAda lebih banyak fungsi dan konstanta dalam objek `Math`, termasuk trigonometri, yang dapat Anda temukan di [docs untuk objek Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math).\n\n## Kesimpulan\n\nUntuk menulis angka dengan banyak nol:\n\n- Tambahkan `\"e\"` dengan hitungan angka nol ke angka. Seperti: `123e6` sama dengan `123` dengan 6 nol `123000000`.\n- Angka negatif setelah `\"e\"` menyebabkan angka untuk dibagi 1 dengan nol yang diberikan. Contohnya `123e-6` berarti `0.000123` (`123` millionths).\n\nUntuk sistem angka yang berbeda:\n\n- Dapat menulis angka secara langsung dalam sistem hex (`0x`), oktal (`0o`) dan sistem biner (`0b`)\n- `parseInt(str, base)` mem-parsing string `str` menjadi bilangan bulat dalam sistem angka dengan diberikan `base`, `2 ≤ base ≤ 36`.\n- `num.toString(base)` mengonversi angka menjadi string dalam sistem angka dengan diberikan `base`.\n\nUntuk mengkonversi nilai seperti `12pt` dan `100px` menjadi sebuah angka:\n\n- Gunakan `parseInt/parseFloat` untuk konversi \"lembut\", dimana membaca sebuah angka dari string dan mengembalikan nilai yang bisa dibaca sebelum terjadi kesalahan.\n\nUntuk pecahan:\n\n- Bulangan menggunakan `Math.floor`, `Math.ceil`, `Math.trunc`, `Math.round` atau `num.toFixed(presisi)`.\n- Pastikan untuk mengingat ada kehilangan presisi saat bekerja dengan pecahan.\n\nLebih banyak fungsi matematika:\n\n- Lihat objek [Math](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Math) ketika Anda membutuhkannya. Perpustakaannya sangat kecil, tetapi dapat memenuhi kebutuhan dasar.\n"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/_js.view/solution.js",
    "content": "function ucFirst(str) {\n  if (!str) return str;\n\n  return str[0].toUpperCase() + str.slice(1);\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/_js.view/test.js",
    "content": "describe(\"ucFirst\", function() {\n  it('Uppercases the first symbol', function() {\n    assert.strictEqual(ucFirst(\"john\"), \"John\");\n  });\n\n  it(\"Doesn't die on an empty string\", function() {\n    assert.strictEqual(ucFirst(\"\"), \"\");\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/solution.md",
    "content": "Kita tidak dapat \"mengganti\" karakter pertama, karena di Javascript string bersifat tidak dapat berubah.\n\nTetapi kita dapat membuat sebuah string baru berdasarkan yang sudah ada, dengan karakter pertama yang besar:\n\n```js\nlet newStr = str[0].toUpperCase() + str.slice(1);\n```\n\nTetapi ada sedikit masalah. Jika `str` bernilai kosong, maka `str[0]` bernilai `undefined`, dan `undefined` tidak memiliki method `toUpperCase()`. Hal tersebut yang menyebabkan error.\n\nAda dua cara di sini:\n\n1. Gunakan `str.charAt(0)`, karena method ini selalu mengembalikan string (mungkin kosong).\n2. Tambahkan pengecekan string kosong.\n\nBerikut adalah cara yang kedua:\n\n```js run demo\nfunction ucFirst(str) {\n  if (!str) return str;\n\n  return str[0].toUpperCase() + str.slice(1);\n}\n\nalert( ucFirst(\"john\") ); // John\n```\n"
  },
  {
    "path": "1-js/05-data-types/03-string/1-ucfirst/task.md",
    "content": "Nilai penting: 5\n\n---\n\n# Buat karakter pertama menjadi besar\n\nTulislah sebuah fungsi `ucFirst(str)` yang mengembalikan `str` dengan karakter pertama yang besar, sebagai contoh:\n\n```js\nucFirst(\"john\") == \"John\";\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/_js.view/solution.js",
    "content": "function checkSpam(str) {\n  let lowerStr = str.toLowerCase();\n\n  return lowerStr.includes('viagra') || lowerStr.includes('xxx');\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/_js.view/test.js",
    "content": "describe(\"checkSpam\", function() {\n  it('finds spam in \"buy ViAgRA now\"', function() {\n    assert.isTrue(checkSpam('buy ViAgRA now'));\n  });\n\n  it('finds spam in \"free xxxxx\"', function() {\n    assert.isTrue(checkSpam('free xxxxx'));\n  });\n\n  it('no spam in \"innocent rabbit\"', function() {\n    assert.isFalse(checkSpam('innocent rabbit'));\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/solution.md",
    "content": "Untuk membuat pencarian bersifat case-insensitive, mari kita ubah string menjadi huruf kecil sebelum kita lakukan pencarian:\n\n```js run demo\nfunction checkSpam(str) {\n  let lowerStr = str.toLowerCase();\n\n  return lowerStr.includes('viagra') || lowerStr.includes('xxx');\n}\n\nalert( checkSpam('buy ViAgRA now') );\nalert( checkSpam('free xxxxx') );\nalert( checkSpam(\"innocent rabbit\") );\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/2-check-spam/task.md",
    "content": "Nilai kepentingan: 5\n\n---\n\n# Mengecek apakah spam\n\nTulislah sebuah fungsi `checkSpam(str)` yang mengembalikan `true` apabila `str` mengandung 'viagra' atau 'XXX', jika tidak kembalikan `false`.\n\nFungsi yang ditulis harus bersifat case-insensitive:\n\n```js\ncheckSpam('buy ViAgRA now') == true\ncheckSpam('free xxxxx') == true\ncheckSpam(\"innocent rabbit\") == false\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/_js.view/solution.js",
    "content": "function truncate(str, maxlength) {\n  return (str.length > maxlength) ? \n    str.slice(0, maxlength - 1) + '…' : str;\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/_js.view/test.js",
    "content": "describe(\"truncate\", function() {\n  it(\"truncate the long string to the given length (including the ellipsis)\", function() {\n    assert.equal(\n      truncate(\"What I'd like to tell on this topic is:\", 20),\n      \"What I'd like to te…\"\n    );\n  });\n\n  it(\"doesn't change short strings\", function() {\n    assert.equal(\n      truncate(\"Hi everyone!\", 20),\n      \"Hi everyone!\"\n    );\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/solution.md",
    "content": "Panjang maksimum adalah `maxlength`, jadi kita perlu memotongnya menjadi lebih pendek, untuk memberi tempat bagi elipsis.\n\nPerlu diperhatikan bahwa elipsis hanyalah sebuah karakter unicode, bukan tiga karakter titik.\n\n```js run demo\nfunction truncate(str, maxlength) {\n  return (str.length > maxlength) ?\n    str.slice(0, maxlength - 1) + '…' : str;\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/03-string/3-truncate/task.md",
    "content": "Nilai kepentingan: 5\n\n---\n\n# Memotong teks menjadi lebih pendek\n\nBuatlah sebuah fungsi `truncate(str, maxlength)` yang mengecek panjang dari `str` dan, apabila panjangnya melebihi `maxlength` -- ganti akhir dari `str` menjadi karakter elipsis `\"…\"`, supaya panjangnya sama dengan `maxlength`.\n\nHasil kembalian dari fungsi seharusnya string yang dipotong (jika diperlukan).\n\nSebagai contoh:\n\n```js\ntruncate(\"What I'd like to tell on this topic is:\", 20) = \"What I'd like to te…\"\n\ntruncate(\"Hi everyone!\", 20) = \"Hi everyone!\"\n```\n"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/_js.view/solution.js",
    "content": "function extractCurrencyValue(str) {\n  return +str.slice(1);\n}"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/_js.view/test.js",
    "content": "describe(\"extractCurrencyValue\", function() {\n\n  it(\"for the string $120 returns the number 120\", function() {\n    assert.strictEqual(extractCurrencyValue('$120'), 120);\n  });\n\n\n});"
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/03-string/4-extract-currency/task.md",
    "content": "Nilai kepentingan: 4\n\n---\n\n# Ambil uangnya\n\nKita memiliki uang dalam bentuk `\"$120\"`. Yaitu: tanda dolar muncul pertama, lalu diikuti oleh angka.\n\nBuatlah sebuah fungsi `extractCurrencyValue(str)` yang mengambil nilai dari bagian angka string tersebut dan mengembalikannya.\n\nSebagai contoh:\n\n```js\nalert( extractCurrencyValue('$120') === 120 ); // true\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/03-string/article.md",
    "content": "# String\n\nDi Javascript, data teks disimpan sebagai string. Tidak ada tipe data sendiri untuk satu buah karakter.\n\n[UTF-16](https://en.wikipedia.org/wiki/UTF-16) selalu digunakan sebagai format internal string, hal tersebut tidak terikat dengan jenis encoding yang digunakan oleh halaman.\n\n## Petik\n\nMari kita lihat berbagai jenis petik.\n\nString dapat ditutup dengan petik satu, petik dua, maupun backtick:\n\n```js\nlet single = 'single-quoted';\nlet double = \"double-quoted\";\n\nlet backticks = `backticks`;\n```\n\nPetik satu dan petik dua kurang lebih hampir sama. Akan tetapi backtick memiliki perbedaan, yaitu memperbolehkan kita untuk menyisipkan ekspresi ke dalam string, dengan menaruhnya di dalam `${…}`:\n\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nalert(`1 + 2 = ${sum(1, 2)}.`); // 1 + 2 = 3.\n```\n\nKelebihan backtick yang lain yaitu backtick memperbolehkan sebuah string untuk terdiri lebih dari satu baris:\n\n```js run\nlet guestList = `Guests:\n * John\n * Pete\n * Mary\n`;\n\nalert(guestList); // list tamu, dipisahkan per baris\n```\n\nLebih rapi, kan? Tetapi petik satu atau dua tidak bekerja seperti itu.\n\nJika kita coba untuk menggunakan mereka untuk lebih dari satu baris, akan terjadi error:\n\n```js run\nlet guestList = \"Guests: // Error: Unexpected token ILLEGAL\n  * John\";\n```\n\nPetik satu dan petik dua berasal dari masa lalu saat bahasa pemrograman dibuat, dimana kebutuhan untuk string lebih dari satu baris belum dipikirkan. Backtick muncul di kemudian hari, dan lebih fleksibel.\n\nBacktick juga memperbolehkan kita untuk menspesifikasi \"fungsi template\" sebelum backtick pertama. Syntaxnya yaitu: <code>func&#96;string&#96;</code>. Fungsi `func` dipanggil secara otomatis, menerima string dan ekspresi yang berada di dalamnya dan bisa memproses mereka. Ini disebut \"tagged templates\". Fitur ini membuat implementasi kustom templating lebih mudah, tapi jaran dipakai dalam praktik. Kamu bisa membaca lebih tentang ini di [manual](mdn:/JavaScript/Reference/Template_literals#Tagged_templates).\n\n## Karakter spesial\n\nMasih mungkin untuk membuat string dengan banyak baris menggunakan petik satu atau petik dua dengan menggunakan \"karakter newline\", ditulis seperti berikut `\\n`, yang menandakan baris baru:\n\n```js run\nlet guestList = \"Guests:\\n * John\\n * Pete\\n * Mary\";\n\nalert(guestList); // list tamu yang dipisahkan per baris\n```\n\nSebagai contoh, kedua baris berikut sama saja, hanya ditulis dengan cara yang berbeda:\n\n```js run\nlet str1 = \"Hello\\nWorld\"; // dua baris menggunakan \"simbol baris baru\"\n\n// dua baris menggunakan backtick\nlet str2 = `Hello\nWorld`;\n\nalert(str1 == str2); // true\n```\n\nAda karakter spesial lain, tetapi mereka jarang digunakan\n\nBerikut adalah daftar lengkapnya:\n\n| Character | Description |\n|-----------|-------------|\n|`\\n`|Baris baru|\n|`\\r`|Carriage return: tidak digunakan sendiri. File teks milik di Windows menggunakan kombinasi dari dua karakter `\\r\\n` untuk menandakan baris baru.|\n|`\\'`, `\\\"`|Petik-petik|\n|`\\\\`|Backslash|\n|`\\t`|Tab|\n|`\\b`, `\\f`, `\\v`| Backspace, Form Feed, Vertical Tab -- tetap bisa digunakan untuk kompabilitas, sekarang sudah tidak digunakan. |\n|`\\xXX`|Karakter unicode dengan nilai heksadesimal `XX`, misalnya `'\\x7A'` itu sama saja dengan `'z'`.|\n|`\\uXXXX`|Sebuah simbol unicode dengan nilai heksadesimal `XXXX` di dalam encoding UTF-16, sebagai contoh `\\u00A9` -- adalah sebuah unicode untuk simbol copyright `©`. Simbol ini harus terdiri dari 4 digit heksadesimal. |\n|`\\u{X…XXXXXX}` (1 to 6 karakter heksadesimal)|Sebuah simbol unicode dengan encoding UTF-32. Beberapa karakter langka menggunakan dua simbol unicode, yang memakan 4 byte. Dengan cara ini kita dapat menggunakan kode yang panjang. |\n\nContoh dengan unicode:\n\n```js run\nalert( \"\\u00A9\" ); // ©\nalert( \"\\u{20331}\" ); // 佫, sebuah karakter mandarin (unicode panjang)\nalert( \"\\u{1F60D}\" ); // 😍, sebuah simbol wajah tersenyum (unicode panjang lainnya)\n```\n\nKarakter-karakter spesial yang diawali dengan karakter backslash `\\` kadang dipanggil dengan sebutan \"escape character\".\n\nKita kadang dapat menggunakannya apabila kita ingin menggunakan petik di dalam string.\n\nMisalnya:\n\n```js run\nalert( 'I*!*\\'*/!*m the Walrus!' ); // *!*I'm*/!* the Walrus!\n```\n\nSeperti yang kita lihat, kita harus menambahkan backslash di depan petik yang di dalam string `\\'`, karena jika tidak petik tersebut akan menandakan akhir dari sebuah string.\n\nTentu saja, hanya jenis petik yang sama dengan penutup string yang perlu di \"escape\". Jadi, solusi yang lebih elegan yaitu mengganti petik satu menjadi petik dua atau backtick:\n\n```js run\nalert( `I'm the Walrus!` ); // I'm the Walrus!\n```\n\nIngat bahwa backslash `\\` hanya dipakai untuk Javascript agar dapat membaca string dengan benar. Di dalam memori, string tidak memiliki `\\`. Anda dapat melihatnya secara langsung pada contoh `alert` di atas.\n\nTetapi bagaimana jika kita ingin menampilkan backslash `\\` di dalam sebuah string?\n\nHal tersebut bisa dilakukan, tetapi kita harus menulisnya dua kali seperti ini `\\\\`:\n\n```js run\nalert( `The backslash: \\\\` ); // The backslash: \\\n```\n\n## Panjang string\n\nProperti `length` memiliki panjang dari string:\n\n```js run\nalert( `My\\n`.length ); // 3\n```\n\nPerlu diingat bahwa `\\n` adalah sebuah karakter spesial, jadi panjang dari string adalah `3`.\n\n```warn header=\"`length` adalah sebuah properti\"\nOrang dengan latar belakang di bahasa pemrograman lain kadang salah mengetik `str.length()` alih-alih `str.length`. Hal tersebut tidak bekerja.\n\nPerlu diingat bahwa `str.length` adalah properti numerik, bukan sebuah fungsi. Tidak perlu menambahkan kurung di belakangnya.\n```\n\n## Mengakses karakter di dalam string\n\nUntuk mengakses karakter pada posisi `pos`, digunakan kurung kotak `[pos]` atau dengan method [str.charAt(pos)](mdn:js/String/charAt). Karakter pertama dimulai dari posisi ke-0:\n\n```js run\nlet str = `Hello`;\n\n// karakter pertama\nalert( str[0] ); // H\nalert( str.charAt(0) ); // H\n\n// karakter terakhir\nalert( str[str.length - 1] ); // o\n```\n\nKurung kotak adalah cara modern untuk mengakses sebuah karakter, sementara `charAt` ada karena alasan historis.\n\nPerbedaan satu-satunya di antara mereka adalah apabila tidak ada karakter yang ditemukan, `[]` mengembalikan `undefined`, dan `charAt` mengembalikan string kosong:\n\n```js run\nlet str = `Hello`;\n\nalert( str[1000] ); // undefined\nalert( str.charAt(1000) ); // '' (string kosong)\n```\n\nKita juga bisa mengakses karakter per karakter menggunakan sintaks `for..of`:\n\n```js run\nfor (let char of \"Hello\") {\n  alert(char); // H,e,l,l,o (char bernilai \"H\", lalu \"e\", lalu \"l\", dan seterusnya)\n}\n```\n\n## String bersifat tidak dapat diubah\n\nNilai string tidak dapat diubah di Javascript. Tak mungkin mengubah sebuah karakter.\n\nMari kita coba buktikan:\n\n```js run\nlet str = 'Hi';\n\nstr[0] = 'h'; // galat\nalert( str[0] ); // tidak bekerja\n```\n\nSalah satu cara untuk mengatasi hal tersebut adalah untuk membuat string baru lalu memasukkan nilainya ke `str`.\n\nSebagai contoh:\n\n```js run\nlet str = 'Hi';\n\nstr = 'h' + str[1]; // mengganti nilai string\n\nalert( str ); // hi\n```\n\nDi bab ini kita akan melihat contoh lebih banyak dari ini.\n\n## Mengganti case\n\nMetode [toLowerCase()](mdn:js/String/toLowerCase) dan [toUpperCase()](mdn:js/String/toUpperCase) mengganti case:\n\n```js run\nalert( 'Interface'.toUpperCase() ); // INTERFACE\nalert( 'Interface'.toLowerCase() ); // interface\n```\n\nAtau, apabila kita hanya ingin sebuah karakter yang diubah menjadi huruf kecil:\n\n```js\nalert( 'Interface'[0].toLowerCase() ); // 'i'\n```\n\n## Mencari substring\n\nAda banyak cara untuk mencari sebuah substring di dalam sebuah string.\n\n### str.indexOf\n\nCara yang pertama yaitu [str.indexOf(substr, pos)](mdn:js/String/indexOf).\n\nMethod ini mencari `substr` di dalam `str`, mulai dari posisi `pos` yang diberikan, dan mengembalikan posisi dimana substring ditemukan atau `-1` jika tidak ditemukan.\n\nMisalnya:\n\n```js run\nlet str = 'Widget with id';\n\nalert( str.indexOf('Widget') ); // 0, karena 'Widget' ditemukan di awal string\nalert( str.indexOf('widget') ); // -1, tidak ditemukan, karena pencarian bersifat case-sensitive\n\nalert( str.indexOf(\"id\") ); // 1, \"id\" ditemukan pada posisi 1 (..idget with id)\n```\n\nParameter kedua yang opsional memperbolehkan kita untuk mencari dari posisi yang ditentukan.\n\nMisalnya, kemunculan pertama `\"id\"` ada di posisi `1`. Untuk mencari kemunculan berikutnya, ayo kita mulai pencarian dari posisi `2`:\n\n```js run\nlet str = 'Widget with id';\n\nalert( str.indexOf('id', 2) ) // 12\n```\n\nJika kita tertarik dengan semua kemunculan, kita bisa menjalankan `indexOf` di dalam loop. Setiap panggilan dibuat dengan posisi setelah kemunculan sebelumnya:\n\n```js run\nlet str = 'As sly as a fox, as strong as an ox';\n\nlet target = 'as'; // mari kita cari\n\nlet pos = 0;\nwhile (true) {\n  let foundPos = str.indexOf(target, pos);\n  if (foundPos == -1) break;\n\n  alert( `Found at ${foundPos}` );\n  pos = foundPos + 1; // lanjutkan pencarian dari posisi berikutnya\n}\n```\n\nAlgoritma yang sama dapat ditulis lebih pendek:\n\n```js run\nlet str = \"As sly as a fox, as strong as an ox\";\nlet target = \"as\";\n\n*!*\nlet pos = -1;\nwhile ((pos = str.indexOf(target, pos + 1)) != -1) {\n  alert( pos );\n}\n*/!*\n```\n\n```smart header=\"`str.lastIndexOf(substr, position)`\"\nAda juga method yang hampir sama [str.lastIndexOf(substr, position)](mdn:js/String/lastIndexOf) yang mencari dari akhir sebuah string sampai ke awalnya.\n\nCara tersebut akan menemukan kemunculan dalam urutan yang terbalik.\n```\n\nAda sedikit kerepotan dalam menggunakan `indexOf` di dalam `if`. Kita tidak dapat menggunakannya seperti ini:\n\n```js run\nlet str = \"Widget with id\";\n\nif (str.indexOf(\"Widget\")) {\n    alert(\"We found it\"); // tidak bekerja!\n}\n```\n\nContoh di atas tidak bekerja karena `str.indexOf(\"Widget\")` mengembalikan `0` (artinya kemunculan ditemukan di awal string). `if` menganggap `0` sebagai `false`.\n\nJadi, kita harus mengecek dengan nilai `-1`, seperti ini:\n\n```js run\nlet str = \"Widget with id\";\n\n*!*\nif (str.indexOf(\"Widget\") != -1) {\n*/!*\n    alert(\"We found it\"); // kalau sekarang berhasil!\n}\n```\n\n#### Trik bitwise NOT\n\nSalah satu trik lama yang digunakan disini adalah operator [bitwise NOT](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators#Bitwise_NOT) `~`. Operator ini mengubah angka menjadi integer 32-bit (menghilangkan bagian desimal jika ada) lalu menegasikan semua bit pada representasi binernya.\n\nDalam praktik, hal tersebut berarti: untuk integer 32-bit `~n` sama dengan `-(n+1)`.\n\nSebagai contoh:\n\n```js run\nalert( ~2 ); // -3, sama dengan -(2+1)\nalert( ~1 ); // -2, sama dengan -(1+1)\nalert( ~0 ); // -1, sama dengan -(0+1)\n*!*\nalert( ~-1 ); // 0, sama dengan -(-1+1)\n*/!*\n```\n\nSeperti yang kita lihat, `~n` bernilai nol apabila `n == -1` (untuk semua signed integer `n`).\n\nJadi, pengecekan di dalam `if ( ~str.indexOf(\"...\") )` bernilai benar apabila hasil dari `indexOf` tidak bernilai `-1`. Dengan kata lain, jika kemunculan ditemukan.\n\nOrang-orang menggunakannya untuk memperpendek pengecekan `indexOf`:\n\n```js run\nlet str = \"Widget\";\n\nif (~str.indexOf(\"Widget\")) {\n  alert( 'Found it!' ); // bekerja\n}\n```\n\nBiasanya tidak direkomendasikan untuk menggunakan fitur bahasa dengan cara yang tidak jelas, tetapi trik ini biasa digunakan di kode yang kuno, jadi kita harus memahaminya.\n\nIngat: `if (~str.indexOf(...))` dibaca sebagai \"if ditemukan\".\n\nUntuk lebih detail, karena bilangan yang besar dipotong menjadi 32-bit oleh operator `~`, ada angka lain yang memberikan hasil `0`, angka yang terkecil yaitu `~4294967295=0`. Hal tersebut menyebabkan trik ini benar apabila string yang dites tidak sepanjang itu.\n\nKita dapat melihat bahwa trik ini hanya digunakan di kode yang kuno, karena Javascript modern menyediakan method `.includes` (lihat di bawah).\n\n### includes, startsWith, endsWith\n\nMethod yang lebih modern [str.includes(substr, pos)](mdn:js/String/includes) mengembalikan `true/false` tergantung dengan apakah `str` mengandung `substr` di dalamnya.\n\nIni adalah pilihan yang cocok apabila kita hanya perlu mengetes apakah `substr` ada, tetapi tidak memerlukan posisinya:\n\n```js run\nalert( \"Widget with id\".includes(\"Widget\") ); // true\n\nalert( \"Hello\".includes(\"Bye\") ); // false\n```\n\nParameter opsinal kedua dari `str.includes` adalah posisi dimana pencarian mulai dilakukan:\n\n```js run\nalert( \"Widget\".includes(\"id\") ); // true\nalert( \"Widget\".includes(\"id\", 3) ); // false, dari posisi 3 tidak ditemukan \"id\"\n```\n\nMethod [str.startsWith](mdn:js/String/startsWith) dan [str.endsWith](mdn:js/String/endsWith) melakukan fungsi seperti namanya:\n\n```js run\nalert( \"Widget\".startsWith(\"Wid\") ); // true, \"Widget\" diawali oleh \"Wid\"\nalert( \"Widget\".endsWith(\"get\") ); // true, \"Widget\" diakhiri oleh \"get\"\n```\n\n## Mengambil substring\n\nAda 3 cara untuk mengambil sebuah substring di Javascript: `substring`, `substr` dan `slice`.\n\n`str.slice(start [, end])`\n: Mengembalikan bagian dari string dari `start` sampai (tapi tidak termasuk) `end`.\n\n    Sebagai contoh:\n\n    ```js run\n    let str = \"stringify\";\n    alert( str.slice(0, 5) ); // 'strin', substring dari posisi 0 sampai 5 (tidak termasuk 5)\n    alert( str.slice(0, 1) ); // 's', dari 0 sampai 1, tetapi tidak termasuk 1, jadi hanya karakter pada posisi 0\n    ```\n\n    Jika tidak ada parameter kedua, maka `slice` akan mengambil semua bagian dari `start` sampai akhir string:\n\n    ```js run\n    let str = \"st*!*ringify*/!*\";\n    alert( str.slice(2) ); // ringify, dari posisi kedua sampai terakhir\n    ```\n\n    Nilai negatif untuk `start/end` juga bisa digunakan. Nilai negatif berarti posisinya dihitung dari akhir string:\n\n    ```js run\n    let str = \"strin*!*gif*/!*y\";\n\n    // mulai dari posisi ke-4 dari kanan, berakhir di posisi pertama dari kanan\n    alert( str.slice(-4, -1) ); // gif\n    ```\n\n`str.substring(start [, end])`\n: Mengembalikan bagian dari string *di antara* `start` dan `end`.\n\n    Method ini hampir sama dengan `slice`, tetapi nilai `start` boleh lebih besar daripada `end`.\n\n    Sebagai contoh:\n\n    ```js run\n    let str = \"st*!*ring*/!*ify\";\n\n    // kedua ini sama saja untuk substring\n    alert( str.substring(2, 6) ); // \"ring\"\n    alert( str.substring(6, 2) ); // \"ring\"\n\n    // ...tetapi tidak untuk slice:\n    alert( str.slice(2, 6) ); // \"ring\" (sama)\n    alert( str.slice(6, 2) ); // \"\" (string kosong)\n\n    ```\n\n    Parameter negatif tidak didukung (tidak seperti slice), mereka dianggap sebagai `0`.\n\n`str.substr(start [, length])`\n: Mengembalikan substring dari `start`, dengan panjang `length`.\n\n    Dibandingkan dengan cara-cara sebelumnya, cara ini memperbolehkan kita untuk menyebutkan `length` alih-alih posisi akhir dari string:\n\n    ```js run\n    let str = \"st*!*ring*/!*ify\";\n    alert( str.substr(2, 4) ); // ring, dari posisi ke-2 ambil 4 karakter\n    ```\n\n    Parameter pertama mungkin bernilai negatif, untuk menghitung dari akhir string:\n\n    ```js run\n    let str = \"strin*!*gi*/!*fy\";\n    alert( str.substr(-4, 2) ); // gi, dari posisi ke-4 ambil 2 karakter\n    ```\n\nMari kita review cara-cara tersebut untuk menghindari kebingungan:\n\n| method | mengambil... | negatives |\n|--------|-----------|-----------|\n| `slice(start, end)` | dari `start` sampai `end` (tidak termasuk `end`) | nilai negatif diperbolehkan |\n| `substring(start, end)` | antara `start` dan `end` | nilai negatif berarti mean `0` |\n| `substr(start, length)` | dari `start` ambil `length` karakter | `start` negatif diperbolehkan |\n\n```smart header=\"Cara mana yang harus kita gunakan?\"\nSemuanya dapat melakukan pekerjaannya. Secara formal, `substr` memiliki kekurangan: fungsi ini tidak tercantum di spesifikasi inti Javascript, tetapi di Annex B, yang mencakup hanya fitur browser yang ada karena alasan historis. Jadi, environment non-browser mungkin gagal untuk mendukungnya. Tetapi dalam praktik fungsi ini bekerja di mana saja.\n\nDibandingkan dengan dua varian yang lain, `slice` lebih fleksibel, karena memperbolehkan parameter negatif dan lebih pendek untuk ditulis. Jadi, dari ketiga cara sudah cukup untuk mengingat `slice`.\n```\n\n## Membandingkan string\n\nSeperti yang kita tahu dari bab <info:comparison>, string dibandingkan karakter per karakter dengan urutan alfabet.\n\nAkan tetapi, ada beberapa pengecualian.\n\n1. Huruf kecil selalu lebih besar dibanding huruf kapital:\n\n    ```js run\n    alert( 'a' > 'Z' ); // true\n    ```\n\n2. Karakter dengan tanda diakritik \"tidak sesuai urutan\":\n\n    ```js run\n    alert( 'Österreich' > 'Zealand' ); // true\n    ```\n\n    Hal ini dapat menyebabkan hasil yang aneh apabila kita mengurutkan nama-nama negara. Biasanya orang mengharapkan `Zealand` muncul setelah `Österreich` di daftar.\n\nUntuk memahami apa yang terjadi, mari kita review representasi internal string di Javascript.\n\nSemua string menggunakan encoding [UTF-16](https://en.wikipedia.org/wiki/UTF-16). Yaitu: setiap karakter memiliki masing-masing kode numerik. Ada method spesial yang memperbolehkan kita untuk mengambil karakter dari kode dan sebaliknya.\n\n`str.codePointAt(pos)`\n: Mengembalikan kode untuk karakter pada posisi `pos`:\n\n    ```js run\n    // karakter dengan case yang berbeda memiliki kode berbeda\n    alert( \"z\".codePointAt(0) ); // 122\n    alert( \"Z\".codePointAt(0) ); // 90\n    ```\n\n`String.fromCodePoint(code)`\n: Membuat sebuah karakter berdasarkan `code` numeriknya\n\n    ```js run\n    alert( String.fromCodePoint(90) ); // Z\n    ```\n\n    Kita juga dapat membuat karakter unicode dengan kode mereka menggunakan `\\u` yang diikuti oleh kode heksadesimal:\n\n    ```js run\n    // 90 bernilai 5a di dalam sistem heksadesimal\n    alert( '\\u005a' ); // Z\n    ```\n\nSekarang mari kita lihat karakter dengan kode di antara `65..220` (alfabet latin dan sedikit tambahan) dengan membuat string yang terdiri dari mereka:\n\n```js run\nlet str = '';\n\nfor (let i = 65; i <= 220; i++) {\n  str += String.fromCodePoint(i);\n}\nalert( str );\n// ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\n// ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜ\n```\n\nKan? Huruf kapital muncul pertama, lalu beberapa karakter spesial, lalu huruf kecil, dan `Ö` berada di dekat akhir string.\n\nSekarang terlihat jelas kenapa `a > Z`.\n\nKarakter-karakter dibandingkan berdasarkan kode numeriknya. Kode yang lebih besar berarti karakter tersebut lebih besar. Kode untuk `a` (97) lebih besar dibandingkan dengan kode untuk `Z` (90).\n\n- Semua huruf kecil muncul setelah huruf kapital karena kode mereka lebih besar.\n- Beberapa karakter seperti `Ö` terpisah dari alfabet utama. Disini, kodenya lebih besar dibandingkan semua karakter dari `a` to `z`.\n\n### Perbandingan yang benar\n\nAlgoritma yang \"benar\" untuk melakukan perbandingan string lebih kompleks dari kelihatannya, setiap bahasa memiliki alfabet mereka masing-masing.\n\nJadi, browser harus tahu bahasa yang digunakan untuk perbandingan.\n\nBeruntungnya, semua browser modern (IE10- memerlukan library tambahan [Intl.JS](https://github.com/andyearnshaw/Intl.js/)) mendukung standar internasionalisasi [ECMA 402](http://www.ecma-international.org/ecma-402/1.0/ECMA-402.pdf).\n\nHal tersebut menyediakan cara spesial untuk membandingkan stringi di berbeda bahasa, mengikuti peraturan mereka.\n\nMethod [str.localeCompare(str2)](mdn:js/String/localeCompare) mengembalikan sebuah interger yang menandakan apakah `str` lebih kecil, sama dengan atau lebih besar dari `str2` menurut peraturan-peraturan bahasa:\n\n- Mengembalikan nilai negatif jika `str` lebih kecil dibandingkan `str2`\n- Mengembalikan nilai positif jika `str` lebih besar dibandingkan `str2`\n- Mengembalikan `0` apabila mereka sama.\n\nSeperti contoh:\n\n```js run\nalert( 'Österreich'.localeCompare('Zealand') ); // -1\n```\n\nMethod ini sebenarnya menerima 2 argumen tambahan yang disebutkan di [dokumentasi](mdn:js/String/localeCompare), yang memperbolehkan untuk menyebutkan bahasa (yang biasanya diambil dari environment, urutan huruf bergantung dari bahasa) dan menyebutkan peraturan-peraturan tambahan seperti case sensitivity atau apakah `\"a\"` and `\"á\"` dianggap sama dan seterusnya.\n\n## Bagian internal dari unicode\n\n```warn header=\"Pengetahuan lanjutan\"\nBagian ini membahas lebih dalam tentang bagian internal string. Pengetahuan ini akan berguna apabila Anda akan berurusan dengan emoji, simbol matematika, hieroglif, atau simbol-simbol lain yang langka.\n\nAnda dapat melewati bagian ini jika tidak berurusan dengan mereka.\n```\n\n### Surrogate pairs\n\nSemua karakter yang sering digunakan memiliki kode 2-byte. Huruf di kebanyakan negara eropa, angka, dan bahkan kebanyakan hieroglif, memiliki representasi 2-byte.\n\nTetapi 2 byte hanya memperbolehkan 65536 kombinasi dan itu tidak cukup untuk semua kombinasi simbol. Jadi simbol-simbol yang langka menggunakan encoding dengan sepasang karakter 2-byte yang disebut \"surrogate pair\".\n\nPanjang dari simbol tersebut adalah `2`:\n\n```js run\nalert( '𝒳'.length ); // 2, SIMBOL MATEMATIKA X BESAR\nalert( '😂'.length ); // 2, MUKA DENGAN TANGISAN BAHAGIA\nalert( '𩷶'.length ); // 2, karakter mandarin\n```\n\nPerlu diingat bahwa surrogate pair tidak ada pada saat Javascript dibuat, oleh karena itu fitur ini tidak diproses secara benar oleh bahasa ini!\n\nKita sebenarnya memiliki sebuah simbol di setiap string di atas, tetapi `length` menunjukkan panjang `2`.\n\n`String.fromCodePoint` dan `str.codePointAt` adalah beberapa method yang menangani surrogate pair dengan benar. Belum lama ini mereka muncul di bahasa ini. Sebelum mereka, hanya ada [String.fromCharCode](mdn:js/String/fromCharCode) dan [str.charCodeAt](mdn:js/String/charCodeAt). Method-method tersebut sebenarnya sama saja dengan `fromCodePoint/codePointAt`, tetapi tidak bisa digunakan untuk surrogate pair.\n\nMengambil sebuah simbol terkadang agak susah, karena surrogate pair diperlakukan sebagai dua karakter:\n\n```js run\nalert( '𝒳'[0] ); // simbol aneh...\nalert( '𝒳'[1] ); // ...bagian dari surrogate pair\n```\n\nPerlu diingat bahwa bagian dari surrogate pair tidak memiliki arti tanpa pasangan yang lain. Jadi contoh di atas menampilkan karakter aneh.\n\nSecara teknis, surrogate pair juga dapat dideteksi berdasarkan kode mereka, jika sebuah karakter memiliki kode di antara `0xd800..0xdbff`, maka karakter ini adalah bagian pertama dari surrogate pair. Karakter selanjutnya (bagian kedua) harus berada di antara `0xdc00..0xdfff`. Interval ini sudah dipesan secara khusus untuk surrogate pair oleh standar.\n\nPada kasus diatas:\n\n```js run\n// charCodeAt is not surrogate-pair aware, so it gives codes for parts\n\nalert( '𝒳'.charCodeAt(0).toString(16) ); // d835, diantara 0xd800 dan 0xdbff\nalert( '𝒳'.charCodeAt(1).toString(16) ); // dcb3, diantara 0xdc00 dan 0xdfff\n```\n\nAnda akan menemukan cara lain untuk bertanganan dengan surrogate pair nanti di bab <info:iterable>. Mungkin juga ada library-library yang untuk hal tersebut, tetapi tidak ada yang cukup terkenal untuk disarankan di sini.\n\n### Tanda diakritik dan normalisasi\n\nDi banyak bahasa terdapat simbol-simbol yang terdiri dari huruf dasar dengan tanda di atas/bawahnya.\n\nSebagai contoh, karakter `a` dapat menjadi huruf dasar untuk: `àáâäãåā`. Kebanyakan karakter \"komposit\" memiliki kode mereka sendiri di tabel UTF-16. Hal tersebut tidak selalu terjadi, karena terlalu banyak kemungkinan kombinasi.\n\nUntuk mendukung komposisi yang fleksibel, UTF-16 memperbolehkan kita untuk menggunakan beberapa karakter unicode: sebuah huruf dasar yang diikuti oleh satu atau lebih karakter \"tanda\" yang \"menghiasinya\".\n\nSebagai contoh, jika kita memiliki `S` diikuti dengan karakter spesial \"titik di atas\" (kode `\\u0307`), maka akan ditampilkan Ṡ.\n\n```js run\nalert( 'S\\u0307' ); // Ṡ\n```\n\nJika kita memerlukan tanda tambahan di atas huruf (atau di bawahnya) -- tidak masalah, tambahkan saja karakter tanda yang diperlukan.\n\nSebagai contoh, jika kita tambahkan sebuah karakter \"titik di bawah\" (kode `\\u0323`), maka kita akan mendapatkan \"S dengan titik di atas dan di bawah\": `Ṩ`.\n\nSebagai contoh:\n\n```js run\nalert( 'S\\u0307\\u0323' ); // Ṩ\n```\n\nHal tersebut memberikan banyak fleksibilitas, tetapi juga masalah yang menarik: dua karakter mungkin terlihat sama, tetapi dapat direpresentasikan dengan komposisi unicode yang berbeda.\n\nSebagai contoh:\n\n```js run\nlet s1 = 'S\\u0307\\u0323'; // Ṩ, S + titik di atas + titik di bawah\nlet s2 = 'S\\u0323\\u0307'; // Ṩ, S + titik di atas + titik di bawah\n\nalert( `s1: ${s1}, s2: ${s2}` );\n\nalert( s1 == s2 ); // false walaupun karakter terlihat sama (?!)\n```\n\nUntuk menyelesaikan masalah ini, terdapat sebuah algoritma \"normalisasi unicode\" yang membuat setiap string menjadi satu bentuk \"normal\".\n\nAlgoritma tersebut diimplementasikan oleh [str.normalize()](mdn:js/String/normalize).\n\n```js run\nalert( \"S\\u0307\\u0323\".normalize() == \"S\\u0323\\u0307\".normalize() ); // true\n```\n\nAgak lucu bahwa di situasi kita `normalize()` menjadikan sekumpulan karakter dengan panjang 3 menjadi satu: `\\u1e68` (S dengan dua titik).\n\n```js run\nalert( \"S\\u0307\\u0323\".normalize().length ); // 1\n\nalert( \"S\\u0307\\u0323\".normalize() == \"\\u1e68\" ); // true\n```\n\nPada kenyataan, hal ini tidak selalu berlaku. Contoh diatas berlaku karena simbol `Ṩ` is \"cukup sering digunakan\", jadi pembuat UTF-16 memasukkannya di tabel utama dan memberinya sebuah kode.\n\nJika Anda ingin belajar lebih lanjut tentang aturan normalisasi dan variasinya -- mereka dideskripsikan di appendix Unicode standard:  [Unicode Normalization Forms](http://www.unicode.org/reports/tr15/), tetapi untuk kebanyakan kasus informasi yang terdapat di bagian ini sudah cukup.\n\n## Kesimpulan\n\n- Terdapat 3 jenis tanda petik. Backtick membolehkan string memiliki baris ganda dan menyisipkan expresi `${…}`.\n- String di Javascript diencode menggunakan UTF-16.\n- Kita bisa memakai karakter seperti `\\n` dan memasukkan karakter berdasarkan unicode mereka menggunakan `\\u...`.\n- Untuk mendapatkan karakter, gunakan: `[]`.\n- Untuk mendapatkan substring, gunakan: `slice` atau `substring`.\n- Untuk mengubah case kecil/besar dari string, gunakan: `toLowerCase/toUpperCase`.\n- Untuk mencari substring, gunakan: `indexOf`, atau `includes/startsWith/endsWith` untuk pengecekan sederhana.\n- Untuk membandingkan string mengikuti bahasa, gunakan `localeCompare`, jika tidak mereka akan dibandingkan berdasarkan kode karakter.\n\nAda beberapa metode string lain yang berguna:\n\n- `str.trim()` -- menghilangkan (\"memotong\") spasi dari awal dan akhir dari sebuah string.\n- `str.repeat(n)` -- mengulang string sebanyak `n` kali.\n- ...dan masih banyak lagi yang dapat ditemukan di dalam [manual](mdn:js/String).\n\nString juga memiliki method-method untuk mencari/mengganti dengan ekspresi reguler (regular expression). Tetapi itu adalah topik yang luas, jadi topik ini dibahas di bagiannya sendiri <info:regular-expressions>.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/1-item-value/solution.md",
    "content": "The result is `4`:\n\n\n```js run\nlet fruits = [\"Apples\", \"Pear\", \"Orange\"];\n\nlet shoppingCart = fruits;\n\nshoppingCart.push(\"Banana\");\n\n*!*\nalert( fruits.length ); // 4\n*/!*\n```\n\nItu karena *array* adalah objek. Jadi baik `shoppingCart` dan `fruits` mereferensi ke *array* yang sama.\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/1-item-value/task.md",
    "content": "importance: 3\n\n---\n\n# Apakah *array* disalin?\n\nApa yang kode ini akan tunjukkan?\n\n```js\nlet fruits = [\"Apples\", \"Pear\", \"Orange\"];\n\n// push sebuah nilai baru ke \"copy\"\nlet shoppingCart = fruits;\nshoppingCart.push(\"Banana\");\n\n// apa yang ada di dalam fruits?\nalert( fruits.length ); // ?\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/_js.view/solution.js",
    "content": "function getMaxSubSum(arr) {\n  let maxSum = 0;\n  let partialSum = 0;\n\n  for (let item of arr) {\n    partialSum += item;\n    maxSum = Math.max(maxSum, partialSum);\n    if (partialSum < 0) partialSum = 0;\n  }\n  return maxSum;\n}"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/_js.view/test.js",
    "content": "describe(\"getMaxSubSum\", function() {\n  it(\"maximal subsum of [1, 2, 3] equals 6\", function() {\n    assert.equal(getMaxSubSum([1, 2, 3]), 6);\n  });\n\n  it(\"maximal subsum of [-1, 2, 3, -9] equals 5\", function() {\n    assert.equal(getMaxSubSum([-1, 2, 3, -9]), 5);\n  });\n\n  it(\"maximal subsum of [-1, 2, 3, -9, 11] equals 11\", function() {\n    assert.equal(getMaxSubSum([-1, 2, 3, -9, 11]), 11);\n  });\n\n  it(\"maximal subsum of [-2, -1, 1, 2] equals 3\", function() {\n    assert.equal(getMaxSubSum([-2, -1, 1, 2]), 3);\n  });\n\n  it(\"maximal subsum of [100, -9, 2, -3, 5] equals 100\", function() {\n    assert.equal(getMaxSubSum([100, -9, 2, -3, 5]), 100);\n  });\n\n  it(\"maximal subsum of [] equals 0\", function() {\n    assert.equal(getMaxSubSum([]), 0);\n  });\n\n  it(\"maximal subsum of [-1] equals 0\", function() {\n    assert.equal(getMaxSubSum([-1]), 0);\n  });\n\n  it(\"maximal subsum of [-1, -2] equals 0\", function() {\n    assert.equal(getMaxSubSum([-1, -2]), 0);\n  });\n\n  it(\"maximal subsum of [2, -8, 5, -1, 2, -3, 2] equals 6\", function() {\n    assert.equal(getMaxSubSum([2, -8, 5, -1, 2, -3, 2]), 6);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/solution.md",
    "content": "# Solusi lamban\n\nKita dapat menghitung semua semua sub-penjumlahan yang memungkinkan.\n\nCara paling sederhana adalah dengan cara mengambil setiap elemen dan menghitung jumlah semua *subarray* dimulai dari situ.\n\nContohnya, untuk `[-1, 2, 3, -9, 11]`:\n\n```js no-beautify\n// Dimulai dari -1:\n-1\n-1 + 2\n-1 + 2 + 3\n-1 + 2 + 3 + (-9)\n-1 + 2 + 3 + (-9) + 11\n\n// Dimulai dari 2:\n2\n2 + 3\n2 + 3 + (-9)\n2 + 3 + (-9) + 11\n\n// Dimulai dari 3:\n3\n3 + (-9)\n3 + (-9) + 11\n\n// Dimulai dari -9\n-9\n-9 + 11\n\n// Dimulai dari 11\n11\n```\n\nKode tersebut sebenarnya adalah sebuah pengulangan *nested* (atau *nested* loop):  pengulangan eksternal elemen-elemen *array*, dan yang internal menghitung sub-jumlah dengan elemen yang sekarang.\n\n```js run\nfunction getMaxSubSum(arr) {\n  let maxSum = 0; // jika kita tidak mengambil elemen apapaun, angka nol akan dikembalikan\n\n  for (let i = 0; i < arr.length; i++) {\n    let sumFixedStart = 0;\n    for (let j = i; j < arr.length; j++) {\n      sumFixedStart += arr[j];\n      maxSum = Math.max(maxSum, sumFixedStart);\n    }\n  }\n\n  return maxSum;\n}\n\nalert( getMaxSubSum([-1, 2, 3, -9]) ); // 5\nalert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11\nalert( getMaxSubSum([-2, -1, 1, 2]) ); // 3\nalert( getMaxSubSum([1, 2, 3]) ); // 6\nalert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100\n```\n\nSolusi tersebut memiliki waktu penyelesaian [O(n<sup>2</sup>)](https://en.wikipedia.org/wiki/Big_O_notation). Dalam kata lain, jika kita menambah ukuran *array* 2 kali lipat, algoritma akan bekerja 4 kali lipat lebih lama.\n\nUntuk *array* yang besar (1000, 10000 *item* atau lebih) algoritma yang demikian akan mengarah pada kelambanan yang parah.\n\n# Solusi cepat\n\nMari jalankan *array* tersebut dan simpan hasil penjumlahkan parsial elemen yang sekarang di dalam variabel `s`. Jika `s` menjadi negatif pada beberapa titik, maka tugaskan `s=0`. Nilai maksimum dari semua `s` tersebut akan menjadi jawabannya.\n\nJika deskripsi tersebut terlalu samar, mohon perhatikan kodenya, yang ternyata cukup pendek:\n\n```js run demo\nfunction getMaxSubSum(arr) {\n  let maxSum = 0;\n  let partialSum = 0;\n\n  for (let item of arr) { // untuk setiap item arr\n    partialSum += item; // menambahkannya ke partialSum\n    maxSum = Math.max(maxSum, partialSum); // ingat nilai maxksimum\n    if (partialSum < 0) partialSum = 0; // nol jika negatif\n  }\n\n  return maxSum;\n}\n\nalert( getMaxSubSum([-1, 2, 3, -9]) ); // 5\nalert( getMaxSubSum([-1, 2, 3, -9, 11]) ); // 11\nalert( getMaxSubSum([-2, -1, 1, 2]) ); // 3\nalert( getMaxSubSum([100, -9, 2, -3, 5]) ); // 100\nalert( getMaxSubSum([1, 2, 3]) ); // 6\nalert( getMaxSubSum([-1, -2, -3]) ); // 0\n```\n\nAlgoritma tersebut membutuhkan tepat 1 *array* yang lolos, jadi waktu penyelesaian adalah O(n).\n\nKamu dapat menemukan informasi yang lebih rinci tentang algoritma di sini: [Masalah *subarray* maksimum](http://en.wikipedia.org/wiki/Maximum_subarray_problem). Jika masih kurang jelas bagaimana hal tersebut bekerja, maka mohon menelusuri algoritma pada contoh di atas, perhatikan bagaimana algoritmanya bekerja, itulah cara yang paling baik.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/10-maximal-subarray/task.md",
    "content": "importance: 2\n\n---\n\n# *Subarray* maksimum\n\nInput adalah sebuah *array* angka, e.g. `arr = [1, -2, 3, 4, -9, 6]`.\n\nTugasnya adalah: menemukan *subarray* `arr` yang berdampingan dengan nilai maksimal penjumlahan *item* yang ada.\n\nTulis fungsi `getMaxSubSum(arr)` yang akan mengembalikan nilai penjumlahan tersebut.\n\nSebagai contoh: \n\n```js\ngetMaxSubSum([-1, *!*2, 3*/!*, -9]) == 5 (the sum of highlighted items)\ngetMaxSubSum([*!*2, -1, 2, 3*/!*, -9]) == 6\ngetMaxSubSum([-1, 2, 3, -9, *!*11*/!*]) == 11\ngetMaxSubSum([-2, -1, *!*1, 2*/!*]) == 3\ngetMaxSubSum([*!*100*/!*, -9, 2, -3, 5]) == 100\ngetMaxSubSum([*!*1, 2, 3*/!*]) == 6 (take all)\n```\n\nJika semua *item* adalah negatif, hal tersebut berarti kita tidak mengambil apapun (*subarray* kosong), jadi jumlahnya sama dengan nol:\n\n```js\ngetMaxSubSum([-1, -2, -3]) = 0\n```\n\nMohon coba untuk memikirkan sebuah solusi cepat: [O(n<sup>2</sup>)](https://en.wikipedia.org/wiki/Big_O_notation) atau bahkan O(n) jika bisa.\n"
  },
  {
    "path": "1-js/05-data-types/04-array/2-create-array/solution.md",
    "content": "\n\n```js run\nlet styles = [\"Jazz\", \"Blues\"];\nstyles.push(\"Rock-n-Roll\");\nstyles[Math.floor((styles.length - 1) / 2)] = \"Classics\";\nalert( styles.shift() );\nstyles.unshift(\"Rap\", \"Reggae\");\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/2-create-array/task.md",
    "content": "importance: 5\n\n---\n\n# Operasi *array*.\n\nMari coba 5 operasi *array*.\n\n1. Buat sebuah *array* `styles` dengan elemen \"Jazz\" dan \"Blues\" di dalamnya.\n2. Tambahkan \"Rock-n-Roll\" pada akhir *array*.\n3. Ganti nilai yang berada di tengah menjadi \"Classics\". Kodemu untuk menemukan nilai di tengah harus berhasil untuk *array* sepanjang apapun.\n4. Hilangkan nilai pertama dari *array* lalu tampilkan *array*.\n5. Dahulan *array* dengan `Rap` and `Reggae` di depannya.\n\n*Array* selama proses:\n\n```js no-beautify\nJazz, Blues\nJazz, Blues, Rock-n-Roll\nJazz, Classics, Rock-n-Roll\nClassics, Rock-n-Roll\nRap, Reggae, Classics, Rock-n-Roll\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/3-call-array-this/solution.md",
    "content": "Panggilan `arr[2]()` secara sintaks adalah  `obj[method]()` yang sudah ada dari lama, dalam peran sebagai `obj` kita memiliki `arr`, dan dalam peran sebagai `method` kita memiliki `2`.\n\nJadi kita memiliki sebuah panggilan fungsi `arr[2]` sebagai sebuah metode objek. Secara alami, fungsi terebut menerima `this` yang mereferensikan ke `arr` and menghasilakn *array* berikut:\n\n```js run\nlet arr = [\"a\", \"b\"];\n\narr.push(function() {\n  alert( this );\n})\n\narr[2](); // a,b,function(){...}\n```\n\n*Array* tersebut memiliki 3 nilai: sejak awal *array* tersebut memiliki dua nilai, plus fungsi. \n"
  },
  {
    "path": "1-js/05-data-types/04-array/3-call-array-this/task.md",
    "content": "importance: 5\n\n---\n\n# Panggilan dalam konteks *array*\n\nApa hasilnya? Mengapa demikian?\n\n```js\nlet arr = [\"a\", \"b\"];\n\narr.push(function() {\n  alert( this );\n})\n\narr[2](); // ?\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/5-array-input-sum/solution.md",
    "content": "Tolong perhatikan rincian penting dari solusi yang diberikan berikut. Kita tidak mengonversi `value` menjadi angka secara langsung setelah `prompt`, karena setelah `value = +value` kita tidak bisa memberitahukan sebuah string kosong (tanda berhenti) dari angka nol (angka yang valid). Kita melakukannya setelah langkah tersebut.\n\n\n```js run demo\nfunction sumInput() {\n \n  let numbers = [];\n\n  while (true) {\n\n    let value = prompt(\"A number please?\", 0);\n\n    // perlukah kita batalkan?\n    if (value === \"\" || value === null || !isFinite(value)) break;\n\n    numbers.push(+value);\n  }\n\n  let sum = 0;\n  for (let number of numbers) {\n    sum += number;\n  }\n  return sum;\n}\n\nalert( sumInput() ); \n```\n\n"
  },
  {
    "path": "1-js/05-data-types/04-array/5-array-input-sum/task.md",
    "content": "importance: 4\n\n---\n\n# Menjumlahkan angka yang di-input\n\nTuliskan fungsi `sumInput()` yang:\n\n- Meminta nilai dari dengan menggunakan `prompt` dan menyimpan nilai tersebut dalam *array*.\n- Berhenti meminta nilai dari pengguna ketika pengguna memasukkan nilai non-numerik, sebuah *string* kosong, atau menekan \"Cancel\".\n- Hitung dan mengembalikan hasil penjumlahan *item* dalam *array*.\n\nCatatan. Sebuah nol `0` adalah angka yang valid, mohon tidak menghentikan input pada angka nol.\n\n[demo]\n"
  },
  {
    "path": "1-js/05-data-types/04-array/article.md",
    "content": "# *Array*\n\nObjek mengizinkanmu untuk menyimpan koleksi-koleksi tertulis dari nilai. Itu bagus.\n\nNamun seringkali kita menemukan bahwa kita butuh sebuah *koleksi yang tertata*, dimana kita memiliki elemen ke-1, ke-2, ke-3 dan seterusnya. Sebagai contohnya, kita butuh menyimpan sebuah daftat sesuatu: pengguna, barang, elemen HTML, dan lain-lain.\n\nTidaklah nyaman untuk menggunakan sebuah objek di kasus ini, karena objek tidak menyediakan metode untuk mengelola elemen. Kita tidak bisa memasukkan sebuah properti baru \"di antara\" properti yang sudah dulu ada. Objek memang tidak tujukan untuk penggunaan yang demikian.\n\nADa sebuah struktur data spesial yang dinamakan `Array`, untuk menyimpan koleksi-koleksi yang tertata.\n\n## Deklarasi\n\nTerdapat dua sintaks untuk membuat sebuah *array* kosong:\n\n```js\nlet arr = new Array();\nlet arr = [];\n```\n\nHampir tiap waktu, sintaks kedua lah yang digunakan. Kita bisa menyediakan elemen-elemen awal dalam tanda:\n\n```js\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n```\n\nElemen-elemen *array* itu diberi (urutan) nomor, mulai dari nol.\n\nKita bisa mendapatkan sebuah elemen menggunakan nomor elemen itu dalam tanda kurung siku:\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits[0] ); // Apple\nalert( fruits[1] ); // Orange\nalert( fruits[2] ); // Plum\n```\n\nKita bisa mengganti sebuah elemen:\n\n```js\nfruits[2] = 'Pear'; // kini [\"Apple\", \"Orange\", \"Pear\"]\n```\n\n...Atau menambakan elemen baru ke *array*:\n\n```js\nfruits[3] = 'Lemon'; // kini [\"Apple\", \"Orange\", \"Pear\", \"Lemon\"]\n```\n\nJumlah elemen di dalam sebuah *array* adalah `length` (panjang) dari *array* tersebut:\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits.length ); // 3\n```\n\nKita juga bisa menggunakan `alert` untuk menampilkan keseluruhan *array*.\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\nalert( fruits ); // Apple,Orange,Plum\n```\n\nSebuah *array* dapat menyimpan elemen dari tipe (data) apapun.\n\nContohnya:\n\n```js run no-beautify\n// nilai-nilai campuran\nlet arr = [ 'Apple', { name: 'John' }, true, function() { alert('hello'); } ];\n\n// mendapatkan objek pada indeks 1 dan menampilkan namanya\nalert( arr[1].name ); // John\n\n// mendapatkan fungsi pada indeks 3 dan menjalankannya\narr[3](); // hello\n```\n\n\n````smart header=\"Tanda koma yang membuntuti\"\nSebuah array, seperti halnya sebuh objek, diakhir dengan tanda koma:\n```js\nlet fruits = [\n  \"Apple\",\n  \"Orange\",\n  \"Plum\"*!*,*/!*\n];\n```\n\nGaya \"tanda koma yang membuntuti\" membuat lebih mudah untuk memasukkan/menghilangkan item dari sebuah array, karena semua baris serupa.\n````\n\n\n## Metode *pop*/*push*, *shift*/*unshift*\n\nSebuah [antrian (*queue*)](https://en.wikipedia.org/wiki/Queue_(abstract_data_type)) adalah bentuk penggunaan paling umum sebuah *array*. Dalam *computer science*, hal ini berarti sebuah koleksi elemen yang tertata yang mendukung dua operasi:\n\n- `push` menambahkan sebuah elemen di akhir antrian.\n- `shift` mengambil sebuah elemen di awal antrian, memajukan antrian elemen, jadi elemen urutan ke-2 menjadi urutan pertama1.\n\n![](queue.svg)\n\n*Array* sudah mendukung kedua operasi tersebut.\n\nDalam latihan yang kita sering kali memerlukannya. Contohnya, sebuah antrian pesan yang perlu ditampilkan di layar.\n\nAda kasus kegunaan lain untuk *array* -- struktur data yang dinamakan [tumpukan *(stack)*](https://en.wikipedia.org/wiki/Stack_(abstract_data_type)).\n\n*Stack* mendukung dua operasi, yakni:\n\n- `push` menambahkan sebuah elemen di akhir tumpukan.\n- `pop` mengambil sebuah elemen di akhir tumpukan.\n\nJadi elemen-elemen baru ditambahkan atau diambil selalu dari \"akhir\" tumpukan.\n\nSebuah *stack* biasanya diilustrasikan sebagai sebuah *pack* kartu: kartu-kartu baru ditambahkan di atas tumpukan atau diambil dari atas tumpukan *pack* kartu tersebut:\n\n![](stack.svg)\n\nUntuk *stack*, elemen terakhir yang di-*push* diterima lebih dulu, hal itu juga disebut sebagai prinsip LIFO (*Last-In-First-Out*) atau \"terakhir masuk, pertama keluar\". Sedangkan untuk *queue*, kita memiliki prinsip (*First-In-First-Out*) atau \"pertama masuk, pertama keluar\".\n\n*Array* dalam JavaScript dapat bekerja baik sebagai sebuah *queue* maupun *stack*. Keduanya membuat kamu bisa menambahkan/menghilangkan elemen baik dari/ke awal ataupun akhir.\n\nDalam *computer science* struktur data yang memungkinkan kita bisa melakukan hal-hal demikian disebut sebagai [*deque* (*double-ended queue*)](https://en.wikipedia.org/wiki/Double-ended_queue).\n\n**Metode yang berfungsi dengan bagian akhir dari _array_:**\n\n`pop`\n: Mengekstrak elemen terakhir dari *array* dan mengembalikan hasilnya:\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\", \"Pear\"];\n\n    alert( fruits.pop() ); // menghilangkan \"Pear\" dan memberi alert\n\n    alert( fruits ); // Apple, Orange\n    ```\n\n`push`\n: Mendorong elemen ke bagian akhir *array*:\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\"];\n\n    fruits.push(\"Pear\");\n\n    alert( fruits ); // Apple, Orange, Pear\n    ```\n\n    Panggilan `fruits.push(...)` sama dengan `fruits[fruits.length] = ...`.\n\n**Metode yang berfungsi dengan bagian awal dari _array_:**\n\n`shift`\n: Mengekstrak elemen pertama dari *array* dan mengembalikannya:\n\n    ```js run\n    let fruits = [\"Apple\", \"Orange\", \"Pear\"];\n\n    alert( fruits.shift() ); // menghilangkan Apple dan memberi alert\n\n    alert( fruits ); // Orange, Pear\n    ```\n\n`unshift`\n: Menambahkan elemen ke bagian awal *array*:\n\n    ```js run\n    let fruits = [\"Orange\", \"Pear\"];\n\n    fruits.unshift('Apple');\n\n    alert( fruits ); // Apple, Orange, Pear\n    ```\n\nMetode `push` dan `unshift` dapat menambahkan beberapa elemen sekaligus:\n\n```js run\nlet fruits = [\"Apple\"];\n\nfruits.push(\"Orange\", \"Peach\");\nfruits.unshift(\"Pineapple\", \"Lemon\");\n\n// [\"Pineapple\", \"Lemon\", \"Apple\", \"Orange\", \"Peach\"]\nalert( fruits );\n```\n\n## Internal *array*\n\nSebuah *array* adalah objek yang spesial. Tanda kurung siku digunakan untuk mengakses sebuah properti `arr[0]` sebenarnya berasal dari sintaks objek. Hal itu secara mendasar sama seperti `obj[key]`, dimana `arr` adalah objek, sedangkan angka-angka digunakan selayaknya kunci.\n\nSintaks tersebut memperpanjang objek yang menyediakan metode khusus untuk berfungsi dengan koleksi-koleksi data yang tertata serta properti `length`. Tetapi pada intinya, *array* tetaplah sebuah objek.\n\n\nIngat, ada 7 tipe (data) dasar dalamJavaScript. *Array* adalah sebuah objek dan oleh karena itu berperilaku selayaknya sebuah objek.\n\nSebagai contoh, *array* disalin oleh referensi:\n\n```js run\nlet fruits = [\"Banana\"]\n\nlet arr = fruits; // salinan dari referensi (dua variabel mereferensikan array yang sama)\n\nalert( arr === fruits ); // true\n\narr.push(\"Pear\"); // modiikasi array oleh referensi\n\nalert( fruits ); // Banana, Pear - 2 elemen sekarang\n```\n\n...Namun apa yang membuat *array* benar-benar istimewa adalah representasi internalnya. Mesin berusaha untuk menyimpan elemen *array* ke dalam area memori yang berdampingan, satu dengan yang lainnya, sepereti yang digambarkan pada ilustrasi di bab ini, serta ada cara optimasi lainnya, untuk membuat *array* bekerja lebih cepat lagi.\n\nBut they all break if we quit working with an array as with an \"ordered collection\" and start working with it as if it were a regular object.\n\nContohnya, secara teknis kita dapat melakukan hal berikut ini:\n\n```js\nlet fruits = []; // membuat sebuah array\n\nfruits[99999] = 5; // menugaskan sebuah properti dengan indeks yang jauh lebih panjang dari length/panjang indeks\n\nfruits.age = 25; // membuat sebuah properti dengan nama yang dapat berubah-ubah\n```\n\nHal itu memungkinkan, karena *array* adalah objek pada dasarnya. Kita bisa menambahkan properti apapun ke *array*.\n\nTetapi mesin akan melihat bahwa kita sedang *array* sebagai sebuah objek reguler. Optimasi yang khusus untuk *array* tidaklah cocok untuk kasus demikian dan akan dihentikan serta hilang pula keuntungannya.\n\nCara-cara penggunaan *array* yang salah yakni:\n\n- Menambahkan sebuah properti non-numerik seperti `arr.test = 5`.\n- Membuat celah-celah kosong, seperti: menambahkan `arr[0]` dan kemudian `arr[1000]` (dan tanpa ada apapun di antara kedua indeks tersebut).\n- Mengisi sebuah *array* dalam urutan yang terbalik, seperti `arr[1000]`, `arr[999]` dan seterusnya.\n\nTolong pikirkan *array* sebagai sebuah struktur istimewa untuk bekerja dengan *data yang tersusun*. *Array* menyediakan metode khusus untuk hal tersebut. *Array* dikembangkan secara hati-hati dalam mesin JavaScript untuk bekerja dengan data yang tersusun berdampingan, mohon gunakan *array* dengan cara demikian. Dan jika kamu membutuhkan kunci-kunci yang dapat diubah-ubah, besar kemungkinan bahwa kamu sebenarnya butuh sebuah objek reguler `{}`.\n\n## Performa\n\nMetode `push/pop` berjalan cepat, sedangkan `shift/unshift` lamban.\n\n![](array-speed.svg)\n\nMengapa bekerja dengan bagian akhir sebuah *array* ketimbang bagian depannya? Mari lihat apa yang terjadi sewaktu eksekusi:\n\n```js\nfruits.shift(); // mengambil 1 elemen dari bagian awal\n```\n\nTidaklah cukup mengambil dan menghapus elemen dengan angka `0`. Elemen lainnya juga perlu diberi angka pula.\n\nOperasi `shift` harus menjalankan 3 hal:\n\n1. Menghapus elemen dengan indeks `0`.\n2. Memindahkan semua elemen ke kiri, memberi nomor ulang semua elemen dari indeks `1` ke `0`, dari `2` ke `1` dan seterusnya.\n3. Memperbarui properti `length`.\n\n![](array-shift.svg)\n\n**Semakin banyak elemen dalam _array_, semakin banyak waktu yang dibutuhkan untuk memindahkan elemen-elemen tersebut, semakin banyak operasi dalam memori.**\n\nHal serupa terjadi dengan `unshift`: untuk menambahkan sebuah elemen ke bagian awal *array*, kita perlu pertama-tama memindahkan elemen yang ada ke kanan, menambah indeksnya.\n\nDan ada apa dengan `push/pop`? Metode tersebut tidak perlu untuk memindahkan apapun. Untuk mengekstrak sebuah elemen dari bagian akhir, metode `pop` membersihkan indeks dan memendekkan `length`.\n\nSintaks untuk operasi `pop` yakni:\n\n```js\nfruits.pop(); // mengambil 1 elemen dari bagian akhir\n```\n\n![](array-pop.svg)\n\n**Metode `pop` tidak perlu memindahkan apapun, karena elemen lain tetap pada indeks masing-masing. Itulah mengapa metode tersebut sangat cepat.**\n\nHal serupa juga terjadi dengan metode `push`.\n\n## Pengulangan (*loop*)\n\nSalah satu cara tertua untuk mensirkulasi elemen-elemen *array* adalah dengan pengulangan indeks dengan `for`:\n\n```js run\nlet arr = [\"Apple\", \"Orange\", \"Pear\"];\n\n*!*\nfor (let i = 0; i < arr.length; i++) {\n*/!*\n  alert( arr[i] );\n}\n```\n\nTetapi untuk *array* ada bentuk dari pengulangan lain, `for..of`:\n\n```js run\nlet fruits = [\"Apple\", \"Orange\", \"Plum\"];\n\n// beralih pada elemen array\nfor (let fruit of fruits) {\n  alert( fruit );\n}\n```\n\nMetode `for..of` tidak memberikan akses pada nomor elemen yang sekarang, hanya nilainya saja, tetapi dalam kebanyakan kasus hal tersebut cukup. Dan lebih pendek.\n\nSecara teknis, karena *array* adalah objek, *array* juga memungkinkan untuk menggunakan `for..in`:\n\n```js run\nlet arr = [\"Apple\", \"Orange\", \"Pear\"];\n\n*!*\nfor (let key in arr) {\n*/!*\n  alert( arr[key] ); // Apple, Orange, Pear\n}\n```\n\nNamun sebenernya hal itu adalah ide buruk. Ada beberapa masalah-masalah potensial:\n\n1. Pengulangan `for..in` beralih pada *semua properti*, tidak hanya yang numerik saja.\n\n    Terdapat apa yang disebut sebagai objek \"seperti *array*\" dalam peramban serta lingkungan lainnya, yang *terlihat seperti array*. Objek tersebut, memiliki `length` dan properti yang ber-indeks, namun juga bisa memiliki properti non-numerik lain serta metode-metode, yang mana biasanya tidak kita butuhkan. Pengulangan `for..in` akan mendaftarkan properti tersebut. Jadi kita perlu bekerja dengan objek-objek yang seperti *array*, lalu properti-properti \"ekstra\" ini akan menjadi sebuah masalah.\n\n2. Pengulangan `for..in` dioptimasi untuk objek-objek umum (*generic*), bukan *array*, dan 10-100 kali lebih lambat. Tentu saja, pengulangan tersebut masih begitu cepat. Percepatannya mungkin saja berpengaruh saat kondisi kemacetan saja. Namun tetap kita harus menyadari perbedaannya.\n\nSecara umum, kita tidak seharusnya menggunakan `for..in` *array*.\n\n\n## Sebuah kata untuk \"length\"\n\nProperti `length` secara otomatis diperbarui ketika kita memodifikasi *array*. Lebih tepatnya, `length` bukanlah jumlah nilai yang ada dalam *array*, tapi angka indeks terbesar ditambah satu.\n\nContohnya, sebuah elemen tunggal dengan indeks yang besar juga menghasilkan panjang atau `length` yang besar:\n\n```js run\nlet fruits = [];\nfruits[123] = \"Apple\";\n\nalert( fruits.length ); // 124\n```\n\nCatat bahwa kita biasanya tidak menggunakan *array* seperti itu.\n\nHal menarik lain tentang properti `length` adalah bahwa properti tersebut juga dapat dituliskan.\n\nJika kita menambahnya secara manual, tidak hal menarik yang terjadi. Tapi jika kita menguranginya, *array* tersebut terpotong. Prosesnya tidak dapat dibalikkan (seperti semula), berikut ini contohnya:\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\narr.length = 2; // memotong hingga 2 elemen\nalert( arr ); // [1, 2]\n\narr.length = 5; // mengembalikan length/panjang\nalert( arr[3] ); // undefined: nilai tidak dapat dikembalikan\n```\n\nJadi, cara paling sederhana untuk membersihkan *array* adalah yakni dengan: `arr.length = 0;`.\n\n\n## new Array() [#new-array]\n\nAda satu sintaks lagi untuk membuat sebuah *array*:\n\n```js\nlet arr = *!*new Array*/!*(\"Apple\", \"Pear\", \"etc\");\n```\n\nSintaks tersebut jarang digunakan, karena tanda kurung siku `[]` lebih pendek. Juga terdapat sebuah fitur yang sukar di dalamnya.\n\nJika `new Array` dipanggil dengan sebuah argumen tunggal yang mana adalah sebuah angka, maka sintaks tersebut akan membuat sebuah *array* yang *tanpa elemen, namun dengan panjang sesuai yang diberikan*.\n\nMari lihat bagaimana orang-orang bisa terjebak dengan hal ini:\n\n```js run\nlet arr = new Array(2); // akankah membuat sebuah array berisi [2] ?\n\nalert( arr[0] ); // undefined! tidak ada elemen.\n\nalert( arr.length ); // length atau panjangnya adalah 2\n```\n\nDalam kode di atas, `new Array(number)` memiliki semua elemen yang `undefined`.\n\nUntuk menghindari kejutan-kejutan yang demikian, kita biasanya menggunakan tanda kurung siku, kecuali kita benar-benar tahu kode apa yang sedang kita tulis.\n\n## *Array* multidimensi\n\n*Array* dapat memiliki isi yang juga merupakan *array*. Kita bisa menggunakannya untuk *array* multidimensi, sebagai contoh untuk menyimpan matriks:\n\n```js run\nlet matrix = [\n  [1, 2, 3],\n  [4, 5, 6],\n  [7, 8, 9]\n];\n\nalert( matrix[1][1] ); // 5, elemen pusat\n```\n\n## toString\n\n*Array* memiliki implementasi metode `toString`-nya sendiri yang mengembalikan daftar elemen yang dipisahkan oleh tanda koma.\n\nContoh:\n\n\n```js run\nlet arr = [1, 2, 3];\n\nalert( arr ); // 1,2,3\nalert( String(arr) === '1,2,3' ); // true\n```\n\nCoba juga contoh berikut ini:\n\n```js run\nalert( [] + 1 ); // \"1\"\nalert( [1] + 1 ); // \"11\"\nalert( [1,2] + 1 ); // \"1,21\"\n```\n\n*Array* tidak memiliki `Symbol.toPrimitive`, begitu juga `valueOf` dapat diandalkan, *array* hanya mengimplentasikan konversi`toString`, jadi di sini `[]` menjadi sebuah *string* kosong, `[1]` menjadi `\"1\"` dan `[1,2]` menjadi `\"1,2\"`.\n\nKetika operator tanda tambah biner `\"+\"` menambahkan sesuatu pada sebuah *string*, operator tersebut mengonversi hal yang ditambahkan tadi menjadi sebuah *string* juga, jadi langkah selanjutnya akan terlihat seperti berikut ini:\n\n```js run\nalert( \"\" + 1 ); // \"1\"\nalert( \"1\" + 1 ); // \"11\"\nalert( \"1,2\" + 1 ); // \"1,21\"\n```\n\n## Jangan bandingkan array dengan ==\n\nArray dalam JavaScript, tidak seperti beberapa bahasa pemrograman lainnya, tidak boleh dibandingkan dengan operator `==`.\n\nOperator ini tidak memiliki perlakuan khusus untuk array, ia bekerja dengan mereka seperti pada objek lainnya.\n\nMari kita ingat aturannya:\n\n- Dua objek sama `==` hanya jika mereka merujuk ke objek yang sama.\n- Jika salah satu argumen `==` adalah objek, dan argumen lainnya primitif, objek tersebut akan diubah menjadi primitif, seperti yang dijelaskan pada bab <info: object-toprimitive>.\n- ... Dengan pengecualian `null` dan` undefined` yang sama `==` satu sama lain dan tidak ada yang lain.\n\nPerbandingan ketat `===` bahkan lebih sederhana, karena tidak mengonversi jenis.\n\nJadi, jika kita membandingkan array dengan `==`, keduanya tidak akan pernah sama, kecuali jika kita membandingkan dua variabel yang mereferensikan array yang sama persis.\n\nSebagai contoh:\n```js run\nalert( [] == [] ); // false\nalert( [0] == [0] ); // false\n```\n\nArray ini adalah objek yang secara teknis berbeda. Jadi mereka tidak sama. Operator `==` tidak melakukan perbandingan item-demi-item.\n\nPerbandingan dengan primitif mungkin memberikan hasil yang tampak aneh juga:\n\n```js run\nalert( 0 == [] ); // true\n\nalert('0' == [] ); // false\n```\n\nDi sini, dalam kedua kasus, kami membandingkan primitif dengan objek array. Jadi, array `[]` diubah menjadi primitif untuk tujuan perbandingan dan menjadi string kosong `` '' `.\n\nKemudian proses perbandingan berlanjut dengan primitif, seperti yang dijelaskan dalam bab <info: type-conversion>:\n\n```js run\n// after [] was converted to ''\nalert( 0 == '' ); // true, karena '' diubah menjadi angka 0\n\nalert('0' == '' ); // false, tidak ada konversi tipe data, string berbeda\n```\n\nJadi, bagaimana cara membandingkan array?\n\nSederhana saja: jangan gunakan operator `==`. Sebaliknya, bandingkan item-by-item dalam satu putaran atau gunakan metode iterasi yang dijelaskan di bab berikutnya.\n\n## Kesimpulan\n\n*Array* adalah sebuah objek berjenis khusus, cocok untuk menyimpan dan mengelola data yang tersusun.\n\n- Deklarasinya:\n\n    ```js\n    // tanda kurung siku (seperti biasa)\n    let arr = [item1, item2...];\n\n    // new Array (pengecualian yang jarang)\n    let arr = new Array(item1, item2...);\n    ```\n\n    Panggilan `new Array(number)` membuat sebuah *array* dengan panjang indeks (*length*) yang diberikan, tetapi tanpa elemen.\n\n- Properti `length` adalah panjang *array* atau, lebih tepatnya, angka indeks terakhir plus satu. Hal tersebut secara otomatis diatur dengan metode *array*.\n- Jika kita memendekkan `length` secara manuak, *array* tersebut akan terpotong.\n\nKita bisa menggunakan sebuah *array* sebagai sebuah *deque* dengan operasi sebagai berikut:\n\n- `push(...items)` menambahkan `items` ke bagian akhir *array*.\n- `pop()` menghilangkan elemen dari bagian akhir *array* dan mengembalikannya.\n- `shift()` menghilangkan elemen dari bagian awal *array* dan mengembalikannya.\n- `unshift(...items)` menambahkan `items` ke bagian awal *array*.\n\nUntuk membuat pengulangan (*loop*) elemen *array*:\n  - `for (let i=0; i<arr.length; i++)` -- bekerja paling cepat, kompatibel dengan peramban lama.\n  - `for (let item of arr)` -- sintaks modern untuk *item* saja,\n  - `for (let i in arr)` -- tidak pernah digunakan.\n\nUntuk membandingkan array, jangan gunakan operator `==` (serta `>`, `<` dan lainnya), karena mereka tidak memiliki perlakuan khusus untuk array. Mereka menanganinya sebagai objek apa pun, dan itu bukan yang biasanya kita inginkan.\n\nSebagai gantinya kamu bisa menggunakan loop `for..of` untuk membandingkan array item-by-item.\n\nKita akan melanjutkan dengan array dan mempelajari lebih banyak metode untuk menambah, menghapus, mengekstrak elemen dan mengurutkan array di bab berikutnya <info: array-methods>.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/_js.view/solution.js",
    "content": "function camelize(str) {\n  return str\n    .split('-') // pecah 'my-long-word' menjadi array ['my', 'long', 'word']\n    .map(\n      // Ubah huruf pertama dari setiap item array menjadi huruf kapital kecuali item pertama\n      // Ubah ['my', 'long', 'word'] into ['my', 'Long', 'Word']\n      (word, index) => index == 0 ? word : word[0].toUpperCase() + word.slice(1)\n    )\n    .join(''); // satukan ['my', 'Long', 'Word'] menjadi 'myLongWord'\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/_js.view/test.js",
    "content": "describe(\"camelize\", function() {\n\n  it(\"leaves an empty line as is\", function() {\n    assert.equal(camelize(\"\"), \"\");\n  });\n\n  it(\"turns background-color into backgroundColor\", function() {\n    assert.equal(camelize(\"background-color\"), \"backgroundColor\");\n  });\n\n  it(\"turns list-style-image into listStyleImage\", function() {\n    assert.equal(camelize(\"list-style-image\"), \"listStyleImage\");\n  });\n\n  it(\"turns -webkit-transition into WebkitTransition\", function() {\n    assert.equal(camelize(\"-webkit-transition\"), \"WebkitTransition\");\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/05-array-methods/1-camelcase/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Ubah border-left-width menjadi borderLeftWidth\n\nTulis sebuah fungsi `cameize(str)` yang mengubah tulisan dengan tanda garin seperti \"my-short-string\" menjadi camel-case \"myShortString\".\n\nItu saja: hapus semua tanda garis, setiap kata setelah tanda garis menjadi camel-case.\n\nContoh:\n\n```js\ncamelize(\"background-color\") == 'backgroundColor';\ncamelize(\"list-style-image\") == 'listStyleImage';\ncamelize(\"-webkit-transition\") == 'WebkitTransition';\n```\n\nCatatan. Hint: gunakan `split` untuk memisahkan string menjadi array, ubah itu dan gunakan `join` untuk menyatukannya.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/10-average-age/solution.md",
    "content": "```js run\nfunction getAverageAge(users) {\n  return users.reduce((prev, user) => prev + user.age, 0) / users.length;\n}\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 29 };\n\nlet arr = [ john, pete, mary ];\n\nalert( getAverageAge(arr) ); // 28\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/10-average-age/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Dapatkan rata-rata umur\n\nTulis fungsi `getAverageAge(users)` yang menerima sebuah array dari objek dengan properti `age` dan mengembalikan rata-rata umur.\n\nRumus dari rata-rata adalah `(age1 + age2 + ... + ageN) / N`.\n\nContoh:\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 29 };\n\nlet arr = [ john, pete, mary ];\n\nalert( getAverageAge(arr) ); // (25 + 30 + 29) / 3 = 28\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/_js.view/solution.js",
    "content": "function unique(arr) {\n  let result = [];\n\n  for (let str of arr) {\n    if (!result.includes(str)) {\n      result.push(str);\n    }\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/_js.view/test.js",
    "content": "describe(\"unique\", function() {\n  it(\"removes non-unique elements\", function() {\n    let strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n      \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n    ];\n\n    assert.deepEqual(unique(strings), [\"Hare\", \"Krishna\", \":-O\"]);\n  });\n\n  it(\"does not change the source array\", function() {\n    let strings = [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"];\n    unique(strings);\n    assert.deepEqual(strings, [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"]);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/solution.md",
    "content": "Kita lihat seluruh item didalam array:\n- Untuk setiap item kita memeriksa apakah array keluaran sudah memiliki itemnya.\n- Jika sudah maka abaikan, sebaliknya tambahkan kedalam array keluaran.\n\n```js run demo\nfunction unique(arr) {\n  let result = [];\n\n  for (let str of arr) {\n    if (!result.includes(str)) {\n      result.push(str);\n    }\n  }\n\n  return result;\n}\n\nlet strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(strings) ); // Hare, Krishna, :-O\n```\n\nKodenya bekerja, tapi terdapat sebuah masalah performansi didalamnya.\n\nMetode `result.includes(str)` secara internal menyusuri arrau `result` dan membandingkan setiap elemen dengan `str` untuk menemukan apakah ada yang sama.\n\nJadi jika didalam `result` terdapat `100` elemen dan tidak ada yang sama dengan `str`, lalu itu akan menyusuri seluruh `result` dan melakukan tepat `100` perbandingan. Dan jika `result` berukuran sangat besar, seperti `10000`, maka akan terjadi `10000` perbandingan.\n\nItu bukanlah masalah bagi mesinnya, karena mesin Javascript sangatlah cepat, jadi menyusuri `10000` array hanya akan terjadi secara microseconds (micro detik).\n\nTapi kita melakukan test untuk setiap elemen dari `arr`, didalam perulangan `for`.\n\nJadi jika `arr.length` adalah `10000` kita akan memiliki sesuatu seperti `10000*10000` = 100 juta perbandingan. Itu sangatlah banyak.\n\nDemikian, solusi ini hanya bagus untuk array dengan ukuran kecil.\n\nSelanjutnya didalam bab <info:map-set> kita akan melihat bagaimana cara mengoptimasinya.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/11-array-unique/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Filter untuk anggota array yang unik\n\nBuatlah sebuah array `arr`.\n\nBuatlah sebuah fungsi `unique(arr)` yang harus mengembalikan array dengan item yang unik dari `arr`.\n\nContoh:\n\n```js\nfunction unique(arr) {\n  /* kodemu */\n}\n\nlet strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(strings) ); // Hare, Krishna, :-O\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/solution.js",
    "content": "function groupById(array) {\n  return array.reduce((obj, value) => {\n    obj[value.id] = value;\n    return obj;\n  }, {})\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/_js.view/test.js",
    "content": "describe(\"groupById\", function() {\n\n  it(\"creates an object grouped by id\", function() {\n    let users = [\n      {id: 'john', name: \"John Smith\", age: 20},\n      {id: 'ann', name: \"Ann Smith\", age: 24},\n      {id: 'pete', name: \"Pete Peterson\", age: 31},\n    ];\n\n    assert.deepEqual(groupById(users), {\n      john: {id: 'john', name: \"John Smith\", age: 20},\n      ann: {id: 'ann', name: \"Ann Smith\", age: 24},\n      pete: {id: 'pete', name: \"Pete Peterson\", age: 31},\n    });\n  });\n\n  it(\"works with an empty array\", function() {\n    users = [];\n    assert.deepEqual(groupById(users), {});\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/05-array-methods/12-reduce-object/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Buatlah objek dengan kunci dari array\n\nAnggaplah kita menerima sebuah array dari user didalam form `{id:..., name:..., age... }`.\n\nBuatlah sebuah fungsi `groupById(arr)` yang membuat sebuah objek, dengan `id` sebagai key/kunci, dan item array sebagai nilai\n\nContoh:\n\n```js\nlet users = [\n  {id: 'john', name: \"John Smith\", age: 20},\n  {id: 'ann', name: \"Ann Smith\", age: 24},\n  {id: 'pete', name: \"Pete Peterson\", age: 31},\n];\n\nlet usersById = groupById(users);\n\n/*\n// Setelah pemanggilan kita harus mempunyai:\n\nusersById = {\n  john: {id: 'john', name: \"John Smith\", age: 20},\n  ann: {id: 'ann', name: \"Ann Smith\", age: 24},\n  pete: {id: 'pete', name: \"Pete Peterson\", age: 31},\n}\n*/\n```\n\nFungsi seperti itu sangat berguna ketika bekerja dengan data dari server.\n\nDitugas ini kita asumsikan bahwa `id` adalah unik. Tidak mungkin memiliki dua item array dengan `id` yang sama.\n\nTolong gunakan metode array `.reduce` didalam solusi."
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/_js.view/solution.js",
    "content": "\nfunction filterRange(arr, a, b) {\n  // menambahkan kurung di sekitar ekspresi untuk membuat keterbacaan menjadi lebih baik\n  return arr.filter(item => (a <= item && item <= b));\n}"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/_js.view/test.js",
    "content": "describe(\"filterRange\", function() {\n\n  it(\"returns the filtered values\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    let filtered = filterRange(arr, 1, 4); \n\n    assert.deepEqual(filtered, [3, 1]);\n  });\n\n  it(\"doesn't change the array\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    let filtered = filterRange(arr, 1, 4); \n\n    assert.deepEqual(arr, [5,3,8,1]);\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/solution.md",
    "content": "```js run demo\r\nfunction filterRange(arr, a, b) {\r\n  // menambahkan kurung di sekitar ekspresi untuk membuat keterbacaan menjadi lebih baik\r\n  return arr.filter(item => (a <= item && item <= b));\r\n}\r\n\r\nlet arr = [5, 3, 8, 1];\r\n\r\nlet filtered = filterRange(arr, 1, 4);\r\n\r\nalert( filtered ); // 3,1 (mencocokan nilai)\r\n\r\nalert( arr ); // 5,3,8,1 (tidak diubah)\r\n```\r\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/2-filter-range/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Filter range / menyaring dengan jarak\n\nTulislah sebuah fungsi `filterRange(arr, a, b)` yang mendapatkan sebuah array `arr`, carilah elemen yang berada diantara `a` dan `b` didalamnya dan kembalikanlah array dari mereka.\n\nFungsinya haruslah tidak memodifikasi arraynya. Itu haruslah mengembalikan array baru.\n\nContoh:\n\n```js\nlet arr = [5, 3, 8, 1];\n\nlet filtered = filterRange(arr, 1, 4); \n\nalert( filtered ); // 3,1 (mencocokan nilai)\n\nalert( arr ); // 5,3,8,1 (tidak diubah)\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/solution.js",
    "content": "\nfunction filterRangeInPlace(arr, a, b) {\n\n  for (let i = 0; i < arr.length; i++) {\n    let val = arr[i];\n\n    // hilangkan jika diluar dari interval\n    if (val < a || val > b) {\n      arr.splice(i, 1);\n      i--;\n    }\n  }\n\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js",
    "content": "describe(\"filterRangeInPlace\", function() {\n\n  it(\"returns the filtered values\", function() {\n\n    let arr = [5, 3, 8, 1];\n\n    filterRangeInPlace(arr, 1, 4); \n\n    assert.deepEqual(arr, [3, 1]);\n  });\n\n  it(\"doesn't return anything\", function() {\n    assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4)); \n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/solution.md",
    "content": "```js run demo\r\nfunction filterRangeInPlace(arr, a, b) {\r\n\r\n  for (let i = 0; i < arr.length; i++) {\r\n    let val = arr[i];\r\n\r\n    // hilangkan jika berada diluar interval\r\n    if (val < a || val > b) {\r\n      arr.splice(i, 1);\r\n      i--;\r\n    }\r\n  }\r\n\r\n}\r\n\r\nlet arr = [5, 3, 8, 1];\r\n\r\nfilterRangeInPlace(arr, 1, 4); // hilangkan angkanya kecuali dari 1 to 4\r\n\r\nalert( arr ); // [3, 1]\r\n```\r\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/3-filter-range-in-place/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Filter range \"in place\" / menyaring dengan jarak \"secara langsung\"\n\nTulis sebuah fungsi `filteredRangeInPlace(arr, a, b)` yang mendapatkan array `arr` dan menghilangkan seluruh nilai yang berada diantara `a` dan `b`. Testnya adalah: `a ≤ arr[i] ≤ b`.\n\nFungsinya haruslah hanya memodifikasi arraynya. Itu harus tidak mengembalikan apapun.\n\nFor instance:\n```js\nlet arr = [5, 3, 8, 1];\n\nfilterRangeInPlace(arr, 1, 4); // hapus angka kecuali dari 1 to 4\n\nalert( arr ); // [3, 1]\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/4-sort-back/solution.md",
    "content": "\n\n```js run\nlet arr = [5, 2, 1, -10, 8];\n\narr.sort((a, b) => b - a);\n\nalert( arr );\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/4-sort-back/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Sortir secara menurun\n\n```js\nlet arr = [5, 2, 1, -10, 8];\n\n// ... kodemu untuk menyortir secara menurun\n\nalert( arr ); // 8, 5, 2, 1, -10\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/5-copy-sort-array/solution.md",
    "content": "Kita bisa menggunakan `slice()` untuk membuat salinan dan menjalankan penyortirannya:\n\n```js run\nfunction copySorted(arr) {\n  return arr.slice().sort();\n}\n\nlet arr = [\"HTML\", \"JavaScript\", \"CSS\"];\n\n*!*\nlet sorted = copySorted(arr);\n*/!*\n\nalert( sorted );\nalert( arr );\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/5-copy-sort-array/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Salin dan sortir array\n\nKita mempunyai sebuah array dari string `arr`. Kita ingin mempunyai salinan yang telah disortir, tapi tidak mengubah `arr`.\n\nBuat sebuah fungsi `copySorted(arr)` yang mengembalikan kopiannya.\n\n```js\nlet arr = [\"HTML\", \"JavaScript\", \"CSS\"];\n\nlet sorted = copySorted(arr);\n\nalert( sorted ); // CSS, HTML, JavaScript\nalert( arr ); // HTML, JavaScript, CSS (tidak berubah)\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-array-get-names/solution.md",
    "content": "```js run\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet users = [ john, pete, mary ];\n\nlet names = users.map(item => item.name);\n\nalert( names ); // John, Pete, Mary\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-array-get-names/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Memetakan nama\n\nKamu punya sebuah array dari objek `user`, masing-masing memiliki `user.name`. Tulis kode yang mengubah itu menjadi sebuah array dari nama.\n\nContoh:\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet users = [ john, pete, mary ];\n\nlet names = /* ... Kodemu */\n\nalert( names ); // John, Pete, Mary\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/solution.js",
    "content": "function Calculator() {\n\n  this.methods = {\n    \"-\": (a, b) => a - b,\n    \"+\": (a, b) => a + b\n  };\n\n  this.calculate = function(str) {\n\n    let split = str.split(' '),\n      a = +split[0],\n      op = split[1],\n      b = +split[2];\n\n    if (!this.methods[op] || isNaN(a) || isNaN(b)) {\n      return NaN;\n    }\n\n    return this.methods[op](a, b);\n  };\n\n  this.addMethod = function(name, func) {\n    this.methods[name] = func;\n  };\n}\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/_js.view/test.js",
    "content": "describe(\"Calculator\", function() {\n  let calculator;\n\n  before(function() {\n    calculator = new Calculator;\n  });\n\n  it(\"calculate(12 + 34) = 46\", function() {\n    assert.equal(calculator.calculate(\"12 + 34\"), 46);\n  });\n\n  it(\"calculate(34 - 12) = 22\", function() {\n    assert.equal(calculator.calculate(\"34 - 12\"), 22);\n  });\n\n  it(\"add multiplication: calculate(2 * 3) = 6\", function() {\n    calculator.addMethod(\"*\", (a, b) => a * b);\n    assert.equal(calculator.calculate(\"2 * 3\"), 6);\n  });\n\n  it(\"add power: calculate(2 ** 3) = 8\", function() {\n    calculator.addMethod(\"**\", (a, b) => a ** b);\n    assert.equal(calculator.calculate(\"2 ** 3\"), 8);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/solution.md",
    "content": "\n- Tolong perhatikan bagaimana method disimpan. Mereka secara sederhana ditambahkan kedalam properti `this.methods`.\n- Semua test dan perubahan numerik dilakukan didalam method `calculate`. Selanjutnya mungkin bisa diperluas untuk mendukung lebih banyak ekspresi yang rumit.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/6-calculator-extendable/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Buat sebuah kalkulator yang bisa diperluas\n\nBuat sebuah konstructor fungsi `Calculator` yang membuat objek kalkulator \"yang dapat diperluas\".\n\nTugasnya terdiri dari dua bagian.\n\n1. Pertama, implementasikan method `calculate(str)` yang menerima sebuah string sepertin `\"1 + 2\"` didalam format \"ANGKA operator ANGKA\" (membatasi ruang) dan mengembalikan hasilnya. Harus mengerti tambah `+` dan kurang `-`.\n\n    Contoh penggunaan:\n\n    ```js\n    let calc = new Calculator;\n\n    alert( calc.calculate(\"3 + 7\") ); // 10\n    ```\n\n2. Lalu tambahkan method `addMethod(name, func)` yang mengajarkan operator operasi baru. methodnya akan menerima `name` dan fungsi dengan dua-argumen `func(a, b)` yang mengimplementasikannya.\n\n    Contoh, kita tambah perkalian `*`, pembagian `/` dan pangkat `**`:\n\n    ```js\n    let powerCalc = new Calculator;\n    powerCalc.addMethod(\"*\", (a, b) => a * b);\n    powerCalc.addMethod(\"/\", (a, b) => a / b);\n    powerCalc.addMethod(\"**\", (a, b) => a ** b);\n\n    let result = powerCalc.calculate(\"2 ** 3\");\n    alert( result ); // 8\n    ```\n\n- Tidak ada tanda kurung atau ekspresi yang rumit didalam tugas ini.\n- Angka dan operatornya dibatasi dengan hanya satu spasi.\n- Disana mungkin terdapat penanganan error jika kamu ingin menambahkannya.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/7-map-objects/solution.md",
    "content": "\n```js run no-beautify\nlet john = { name: \"John\", surname: \"Smith\", id: 1 };\nlet pete = { name: \"Pete\", surname: \"Hunt\", id: 2 };\nlet mary = { name: \"Mary\", surname: \"Key\", id: 3 };\n\nlet users = [ john, pete, mary ];\n\n*!*\nlet usersMapped = users.map(user => ({\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n}));\n*/!*\n\n/*\nusersMapped = [\n  { fullName: \"John Smith\", id: 1 },\n  { fullName: \"Pete Hunt\", id: 2 },\n  { fullName: \"Mary Key\", id: 3 }\n]\n*/\n\nalert( usersMapped[0].id ); // 1\nalert( usersMapped[0].fullName ); // John Smith\n```\n\nPerhatikan bahwa didalam fungsi arrow kita butuh untuk menggunakan tambahan kurung.\n\nKita tidak bisa menulis seperti ini:\n```js\nlet usersMapped = users.map(user => *!*{*/!*\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n});\n```\n\nSeperti yang kita ingat, terdapat dua fungsi arrow: tanpa body `value => expression` dan dengan body `value => {...}`.\n\nDisini  Javascript akan memperlakukan `{` seperti awal dari fungsi body, bukan awal dari objek. Solusinya adalah untuk membungkus mereka didalam kurung \"biasa\":\n\n```js\nlet usersMapped = users.map(user => *!*({*/!*\n  fullName: `${user.name} ${user.surname}`,\n  id: user.id\n}));\n```\n\nSekarang beres.\n\n\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/7-map-objects/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Memetakan objek\n\nKamu mempunyai array dari objek `user`, masing-masing memiliki `name`, `surname` dan `id`.\n\nTulis kode untuk membuat array lainnya dari itu, sebuah objek dengan `id` dan `fullName`, dimana` fullName` dibuat dari `name` dan `surname`.\n\nContoh: \n\n```js no-beautify\nlet john = { name: \"John\", surname: \"Smith\", id: 1 };\nlet pete = { name: \"Pete\", surname: \"Hunt\", id: 2 };\nlet mary = { name: \"Mary\", surname: \"Key\", id: 3 };\n\nlet users = [ john, pete, mary ];\n\n*!*\nlet usersMapped = /* ... kodemu ... */\n*/!*\n\n/*\nusersMapped = [\n  { fullName: \"John Smith\", id: 1 },\n  { fullName: \"Pete Hunt\", id: 2 },\n  { fullName: \"Mary Key\", id: 3 }\n]\n*/\n\nalert( usersMapped[0].id ) // 1\nalert( usersMapped[0].fullName ) // John Smith\n```\n\nJadi, sebenarnya kamu harus memetakan satu array dari objek menjadi array dari objek lainnya. Cobalah gunakan `=>` disini. Ada sedikit yang harus ditangkap."
  },
  {
    "path": "1-js/05-data-types/05-array-methods/8-sort-objects/solution.md",
    "content": "```js run no-beautify\nfunction sortByAge(arr) {\n  arr.sort((a, b) => a.age - b.age);\n}\n\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet arr = [ pete, john, mary ];\n\nsortByAge(arr);\n\n// sekarang sudah diurutkan: [john, mary, pete]\nalert(arr[0].name); // John\nalert(arr[1].name); // Mary\nalert(arr[2].name); // Pete\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/8-sort-objects/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Urutkan user dari umur\n\nTulislah fungsi `sortByAge(users)` yang menerima sebuah array dari objek dengan properti `age` dan urutkan mereka berdasarkan `age`.\n\nContoh:\n\n```js no-beautify\nlet john = { name: \"John\", age: 25 };\nlet pete = { name: \"Pete\", age: 30 };\nlet mary = { name: \"Mary\", age: 28 };\n\nlet arr = [ pete, john, mary ];\n\nsortByAge(arr);\n\n// sekarang: [john, mary, pete]\nalert(arr[0].name); // John\nalert(arr[1].name); // Mary\nalert(arr[2].name); // Pete\n```\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/9-shuffle/solution.md",
    "content": "Solusi simpelnya bisa seperti:\n\n```js run\n*!*\nfunction shuffle(array) {\n  array.sort(() => Math.random() - 0.5);\n}\n*/!*\n\nlet arr = [1, 2, 3];\nshuffle(arr);\nalert(arr);\n```\n\nEntah bagaimana kode diatas bekerja, karena `Math.random() - 0.5` adalah angka acak yang mungkin bisa positif atau negatif, jadi fungsi untuk pengurutan menyusun ulang elemen secara acak.\n\nTapi karena fungsi untuk mengurutkan bukan digunakan dengan cara seperti ini, tidak semua permutasi memiliki probabilitas yang sama.\n\nContoh, lihat kode dibawah. Itu akan menjalankan `shuffle` 1000000 kali dan menghitung kemunculan dari seluruh hasil yang mungkin terjadi:\n\n```js run\nfunction shuffle(array) {\n  array.sort(() => Math.random() - 0.5);\n}\n\n// hitung kemunculan dari seluruh permutasi yang mungkin terjadi\nlet count = {\n  '123': 0,\n  '132': 0,\n  '213': 0,\n  '231': 0,\n  '321': 0,\n  '312': 0\n};\n\nfor (let i = 0; i < 1000000; i++) {\n  let array = [1, 2, 3];\n  shuffle(array);\n  count[array.join('')]++;\n}\n\n// tampilkan hitungan permutasi yang mungkin terjadi\nfor (let key in count) {\n  alert(`${key}: ${count[key]}`);\n}\n```\n\nAn example result (depends on JS engine):\n\n```js\n123: 250706\n132: 124425\n213: 249618\n231: 124880\n312: 125148\n321: 125223\n```\n\nKita bisa melihat secara jelas: `123` dan `213` muncul lebih banyak dari lainnya.\n\nHasil dari kodenya mungkin berbeda-beda diantara mesin Javascript, tapi kita sudah bisa melihat pendekatan yang tak bisa diandalkan.\n\nkenapa itu tidak bekerja? Secara umum, `sort` adalah sebuah \"black box\": kita bisa berikan sebuah array dan fungsi perbandingan kedalamnya dan berharap arraynya akan diurutkan. Tapi karena keteracakan dari perbandingan blackbox menjadi tak karuan, dan bagaimana tepatnya itu tergantung dari perbedaan implementasi diantara mesinnya.\n\nTerdapat cara yang lebih baik untuk melakukan tugas seperti itu. Contoh, terdapat algoritma bagus yang dipanggil dengan [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). Idenya adalah untuk membuat array menjadi terbalik dan mengganti setiap elemen dengan elemen acak sebelumnya:\n\n```js\nfunction shuffle(array) {\n  for (let i = array.length - 1; i > 0; i--) {\n    let j = Math.floor(Math.random() * (i + 1)); // index acak dari 0 ke i\n\n    // ganti elemen array[i] dan array[j]\n    // kita gunakan sintaks \"destructuring assignment\" untuk mendapatkannya\n    // kamu akan menemukan lebih banyak detail tentang sintaksnya nanti di bab selanjutnya\n    // bisa juga ditulis seperti:\n    // let t = array[i]; array[i] = array[j]; array[j] = t\n    [array[i], array[j]] = [array[j], array[i]];\n  }\n}\n```\n\npengetesan dengan cara yang sama:\n\n```js run\nfunction shuffle(array) {\n  for (let i = array.length - 1; i > 0; i--) {\n    let j = Math.floor(Math.random() * (i + 1));\n    [array[i], array[j]] = [array[j], array[i]];\n  }\n}\n\n// hitung seluruh kemunculan dari permutasi yang mungkin terjadi\nlet count = {\n  '123': 0,\n  '132': 0,\n  '213': 0,\n  '231': 0,\n  '321': 0,\n  '312': 0\n};\n\nfor (let i = 0; i < 1000000; i++) {\n  let array = [1, 2, 3];\n  shuffle(array);\n  count[array.join('')]++;\n}\n\n// tampilkan perhitungan dari seluruh permutasi yang mungkin terjadi\nfor (let key in count) {\n  alert(`${key}: ${count[key]}`);\n}\n```\n\nContoh keluaran:\n\n```js\n123: 166693\n132: 166647\n213: 166628\n231: 167517\n312: 166199\n321: 166316\n```\n\nSekarang terlihat bagus: seluruh permutasi muncul dengan probabilitas yang sama.\n\nAlso, performance-wise the Fisher-Yates algorithm is much better, there's no \"sorting\" overhead.\nJuga, performansi dari algoritma Fisher-Yates lebih baik, tidak ada \"pengurutan\" tambahan.\n"
  },
  {
    "path": "1-js/05-data-types/05-array-methods/9-shuffle/task.md",
    "content": "nilai penting: 3\n\n---\n\n# Mengacak sebuah array\n\nTulis fungsi `shuffle(array`) yang mengocok (pengurutan secara acak) elemen dari sebuah array.\n\nPemanggilan dari `shuffle` akan menghasilkan urutan yang berbeda-beda. Contoh:\n\n```js\nlet arr = [1, 2, 3];\n\nshuffle(arr);\n// arr = [3, 2, 1]\n\nshuffle(arr);\n// arr = [2, 1, 3]\n\nshuffle(arr);\n// arr = [3, 1, 2]\n// ...\n```\n\nSeluruh pengurutan elemen harus punya probabilitas yang sama. Contoh, `[1,2,3]` bisa di urutkan menjadi `[1,2,3]` atau `[1,3,2]` atau `[3,1,2]` dll, dengan probabilitas yang sama untuk setiap kasus."
  },
  {
    "path": "1-js/05-data-types/05-array-methods/article.md",
    "content": "# Metode *array*\n\n*Array* menyediakan begitu banyak metode. Untuk mempermudah, dalam bab ini metode-metode tersebut dibagi menjadi beberapa kelompok.\n\n## Menambahkan/menghapus *item*\n\nKita sudah tahu metode-metode yang menambahkan dan menghapus *item* dari bagian awal atau akhir *array*:\n\n- `arr.push(...items)` -- menambahkan *item* ke bagian akhir,\n- `arr.pop()` -- mengekstrak sebuah *item* dari bagian akhir,\n- `arr.shift()` -- mengekstrak sebuah *item* dari bagian awal,\n- `arr.unshift(...items)` -- menambahkan *item* ke bagian awal.\n\nBerikut ini ada beberapa metode lainnya.\n\n### *splice*\n\nBagaimana cara untuk menghapus sebuah elemen dari *array*?\n\n*Array* merupakan objek, jadi kita bisa coba menggunakan `delete`:\n\n```js run\nlet arr = [\"I\", \"go\", \"home\"];\n\ndelete arr[1]; // menghapus \"go\"\n\nalert( arr[1] ); // undefined\n\n// kini arr = [\"I\",  , \"home\"];\nalert( arr.length ); // 3\n```\n\nElemen tersebut telah dihapus, tetapi *array* itu masih memiliki 3 elemen, kita bisa melihat bahwa `arr.length == 3`.\n\nHal itu alami, karena `delete obj.key` menghilangkan sebuah nilai berdasarkan `key`. Itulah yang dilakukannya. Tidak masalah bagi objek. Tapi untuk *array* kita biasanya ingin elemen yang tersisa untuk bergeser dan mengisi ruang yang dikosongkan tadi. Kita ingin memiliki sebuah *array* yang lebih pendek sekarang.\n\nJadi, metode khusus harus digunakan.\n\nMetode [arr.splice(start)](mdn:js/Array/splice) adalahs sebuah fungsi serbaguna untuk *array*. *Splice* bisa melakukan banyak hal: memasukkan, menghilangkan serta mengganti elemen.\n\nSintaksnya yakni:\n\n```js\narr.splice(start[, deleteCount, elem1, ..., elemN])\n```\n\n\nDimulai dari posisi `index`: menghapus elemen `deleteCount` dan kemudian memasukkan `elem1, ..., elemN` di tempatnya masing-masing. Mengembalikan *array* yang tersusun atas elemen yang dihapus.\n\n\nMetode ini mudah untuk dipahami melalui contoh.\n\nMari mulai dengan penghapusan:\n\n```js run\nlet arr = [\"I\", \"study\", \"JavaScript\"];\n\n*!*\narr.splice(1, 1); // dari indeks 1 menghapus 1 elemen\n*/!*\n\nalert( arr ); // [\"I\", \"JavaScript\"]\n```\n\nMudah, kan? Mulai dari indeks `1` metode ini menghilangkan elemen di indeks `1`.\n\nDalam contoh selanjutnya kita menghilangkan 3 element dan menggantinya dengan 2 elemen lain:\n\n```js run\nlet arr = [*!*\"I\", \"study\", \"JavaScript\",*/!* \"right\", \"now\"];\n\n// menghilangkan 3 elemen pertama dan menggantinya dengan yang lain\narr.splice(0, 3, \"Let's\", \"dance\");\n\nalert( arr ) // sekarang [*!*\"Let's\", \"dance\"*/!*, \"right\", \"now\"]\n```\n\nDi sini kita dapat melihat bahwa `splice` mengembalikan *array* yang berisi elemen terhapus:\n\n```js run\nlet arr = [*!*\"I\", \"study\",*/!* \"JavaScript\", \"right\", \"now\"];\n\n// menghilangkan 2 elemen pertama\nlet removed = arr.splice(0, 2);\n\nalert( removed ); // \"I\", \"study\" <-- array yang berisi elemen-elemen yang dihapus\n```\n\nMetode `splice` juga mampu memasukkan elemen tanpa menghilangkan elemen apapun yang ada sebelumnya. Untuk itu kita perlu mengatur `deleteCount` menjadi `0`:\n\n```js run\nlet arr = [\"I\", \"study\", \"JavaScript\"];\n\n// dari indeks 2\n// menghapus 0\n// kemudian masukkan \"complex\" dan \"language\"\narr.splice(2, 0, \"complex\", \"language\");\n\nalert( arr ); // \"I\", \"study\", \"complex\", \"language\", \"JavaScript\"\n```\n\n````smart header=\"Indeks berangka negatif diperbolehkan\"\nDi sini dan di metode array lainnya, indeks (berangka) negatif diperbolehkan. Indeks-indeks tersebut menspesifikasikan posisi dari bagian akhir sebuah array, seperti ini:\n\n```js run\nlet arr = [1, 2, 5];\n\n// dari indeks -1 (satu langkah dari bagian akhir)\n// menghaous 0 element,\n// lalu memasukka 3 dan 4\narr.splice(-1, 0, 3, 4);\n\nalert( arr ); // 1,2,3,4,5\n```\n````\n\n### *slice*\n\nMetode [arr.slice](mdn:js/Array/slice) lebih sederhana daripada metode serupa sebelumnya yakni `arr.splice`.\n\nSintaksnya adalah:\n\n```js\narr.slice([start], [end])\n```\n\nMetode ini mengembalikan sebuah sebuah *array* baru hasil salinan semua *item* yang ada dari indeks `start` hingga `end` (indeks `end` tidak termasuk). Baik `start` maupun `end` bisa saja negatif, dalam kasus tersebut posisi dari bagian akhir *array* sudah diasumsikan/diperkirakan.\n\nMirip dengan metode *string* `str.slice`, namun membuat *subarray* bukan *substring*.\n\nContohnya:\n\n```js run\nlet arr = [\"t\", \"e\", \"s\", \"t\"];\n\nalert( arr.slice(1, 3) ); // e,s (disalin dari 1 sampai 3)\n\nalert( arr.slice(-2) ); // s,t (disalin dari -2 sampai bagian akhir)\n```\n\nKita juga bisa memanggil metode tersebut tanpa argumen: `arr.slice()` membuat sebuah salinan dari `arr`. Cara demikian seringkali digunakan untuk mendapatkan sebuah salinan untuk transformasi yang lebih jauh tanpa mempengaruhi *array* yang asli.\n\n### *concat*\n\nMetode [arr.concat](mdn:js/Array/concat) membuat sebuah *array* baru yang sudah termasuk nilai-nilai dari *array* lainnya serta *item-item* tambahan.\n\nSintaksnya sebagai berikut:\n\n```js\narr.concat(arg1, arg2...)\n```\n\nSintaks tersebut menerima berapapun argumen -- bisa jadi *array* atau nilai.\n\nHasilnya adalah sebuah *array* yang berisi *item* dari `arr`, kemudian `arg1`, `arg2` dan sebagainya.\n\nJika sebuah argumen `argN` adalah sebuah *array*, makan semua elemennya akan disalin. Jika tidak, argumen itu sendiri yang akan disalin.\n\nSebagai contoh:\n\n```js run\nlet arr = [1, 2];\n\n// membuat sebuah array dari: arr dan [3,4]\nalert( arr.concat([3, 4]) ); // 1,2,3,4\n\n// membuat sebuah array dari: arr dan [3,4] dan [5,6]\nalert( arr.concat([3, 4], [5, 6]) ); // 1,2,3,4,5,6\n\n// membuat sebuah array dari: arr dan [3,4], lalu menambahkan nilai 5 dan 6\nalert( arr.concat([3, 4], 5, 6) ); // 1,2,3,4,5,6\n```\n\nNormalnya, metode ini hanya menyalin elemen-elemen dari *array*. Objek lainnya, bahkan jika objek-objek tersebut terlihat seperti *array*, akan ditambahkan secara keseluruhan:\n\n```js run\nlet arr = [1, 2];\n\nlet arrayLike = {\n  0: \"something\",\n  length: 1\n};\n\nalert( arr.concat(arrayLike) ); // 1,2,[oobjek Object]\n```\n\n...Tetapi jika sebuah objek mirip *array* memiliki sebuah properti khusus `Symbol.isConcatSpreadable`, maka objek tersebut diperlakukan sebagai sebuah *array* dengan `concat`: elemennya ditambahkan:\n\n```js run\nlet arr = [1, 2];\n\nlet arrayLike = {\n  0: \"something\",\n  1: \"else\",\n*!*\n  [Symbol.isConcatSpreadable]: true,\n*/!*\n  length: 2\n};\n\nalert( arr.concat(arrayLike) ); // 1,2,something,else\n```\n\n## *Iterate*: forEach\n\nMetode [arr.forEach](mdn:js/Array/forEach) membuat kita dapat menjalankan sebuah fungsi untuk setiap elemen yang ada di dalam *array*.\n\nSintaksnya:\n```js\narr.forEach(function(item, index, array) {\n  // ... do something with item\n});\n```\n\nSebagai contoh, kode berikut ini menampilkan tiap elemen dalam *array*:\n\n```js run\n// untuk setiap (for each) elemen memanggil alert\n[\"Bilbo\", \"Gandalf\", \"Nazgul\"].forEach(alert);\n```\n\nDan kode ini lebih rinci tentang posisi elemen-elemen tersebut dalam *array* yang dituju:\n\n```js run\n[\"Bilbo\", \"Gandalf\", \"Nazgul\"].forEach((item, index, array) => {\n  alert(`${item} is at index ${index} in ${array}`);\n});\n```\n\nHasil dari fungsi tersebut (jika mengembalikan sesuatu) dibuang dan diabaikan.\n\n\n## Mencari dalam *array*\n\nKini mari membahas metode-metode yang bertugas mencari dalam *array*.\n\n### *indexOf/lastIndexOf* dan *includes*\n\nMetode [arr.indexOf](mdn:js/Array/indexOf), [arr.lastIndexOf](mdn:js/Array/lastIndexOf) dan [arr.includes](mdn:js/Array/includes) memiliki sintaks yang sama dan pada dasarnya keduanya melakukan fungsi yang samahave the same syntax, namun untuk mengoperasikannya perlu ditujukan *item* bukan karakter:\n\n- `arr.indexOf(item, from)` -- mencari `item` dimulai dari indeks `from`, dan mengembalikan indeks dimana *item* yang dicari itu ditemukan, jika tidak akan mengembalikan `-1`.\n- `arr.lastIndexOf(item, from)` -- serupa, namun mencari dari kiri ke kanan.\n- `arr.includes(item, from)` -- mencari `item` dimulai dari indeks `from`, mengembalkikan `true` jika yang dicari itu ditemukan.\n\nContohnya:\n\n```js run\nlet arr = [1, 0, false];\n\nalert( arr.indexOf(0) ); // 1\nalert( arr.indexOf(false) ); // 2\nalert( arr.indexOf(null) ); // -1\n\nalert( arr.includes(1) ); // true\n```\n\nPerlu diperhatikan bahwa metode tersebut menggunakan perbandingan `===`. Jadi, jika kita mencari `false`, metode ini akan tepat mencari `false` dan bukan nol.\n\nJika kita ingin memeriksa pencantuman, dan tidak ingin tahu indeks yang persis, maka direkomendasikan menggunakan `arr.includes`.\n\nJuga, perbedaan kecil dari `includes` yakni metode ini menangani `NaN` dengan tepat, tidak seperti `indexOf/lastIndexOf`:\n\n```js run\nconst arr = [NaN];\nalert( arr.indexOf(NaN) ); // -1 (seharusnya 0, tetapi tanda persamaan === tidak berfungsi pada NaN)\nalert( arr.includes(NaN) );// true (benar)\n```\n\n### *find* dan *findIndex*\n\nBayangkan kita memiliki sebuah *array* berisi objek. Bagaimana cata kita menemukan sebuah objek dengan kondisi tertentu?\n\nBerikut ini ada metode [arr.find(fn)](mdn:js/Array/find) yang dapat mudah digunakan.\n\nSintaksnya:\n```js\nlet result = arr.find(function(item, index, array) {\n  // jika true dikembalikan, item dikembalikan dan pengulangan dihentinkan\n  // untuk skenario palsu akan mengembalikan undefined\n});\n```\n\nFungsi tersebut dipanggil untuk elemen-elemen dalam *array*, satu sama lainnya:\n\n- `item` adalah elemen.\n- `index` adalah indeks elemen tersebut.\n- `array` adalah *array* itu sendiri.\n\nJika fungsi tersebut mengembalikan `true`, pencarian dihentikan, lalu `item` akan dikembalikan. Jika tidak ditemukan apa-apa, `undefined` yang dikembalikan.\n\nSebagai contoh, kita memiliki sebuah *array* berisi elemen pengguna, tiap pengguna memiliki *field*  `id` dan `name`. Mari cari pengguna dengan `id == 1`:\n\n```js run\nlet users = [\n  {id: 1, name: \"John\"},\n  {id: 2, name: \"Pete\"},\n  {id: 3, name: \"Mary\"}\n];\n\nlet user = users.find(item => item.id == 1);\n\nalert(user.name); // John\n```\n\nPada kehidupan nayata *array* berisi objek adalah hal yang umum, jadi metode `find` sangatlah berguna.\n\nIngat bahwa contoh yang kami berikan untuk mencari (`find`) fungsi `item => item.id == 1` hanya dengan satu argumen. Ini adalah hal umum, argumen lainnya pada fungsi lainnya jarang digunakan.\n\nMetode [arr.findIndex](mdn:js/Array/findIndex) pada dasarnya sama, namun mengembalikan indeks dimana elemen tersebut ditemukan bukan elemen itu sendiri serta mengembalikan `-1` ketika tidak ditemukan apapun.\n\n### *filter*\n\nMetode `find` mencari sebuah elemen tunggal (pertama) yang akan membuat fungsi tersebut mengembalikan `true`.\n\nJika ada banyak elemen demikian, kita bisa menggunakan[arr.filter(fn)](mdn:js/Array/filter).\n\nSintaksnya mirip dengan `find`, tetapi `filter` mengembalikan *array* yang berisi elemen-elemen yang cocok:\n\n```js\nlet results = arr.filter(function(item, index, array) {\n  // jika true item di-push ke hasil dan pengulangan berlanjut\n  // mengembalikan array kosong jika tidak ditemukan apapun\n});\n```\n\nSebagai contoh:\n\n```js run\nlet users = [\n  {id: 1, name: \"John\"},\n  {id: 2, name: \"Pete\"},\n  {id: 3, name: \"Mary\"}\n];\n\n// mengembalikan array dua user pertama\nlet someUsers = users.filter(item => item.id < 3);\n\nalert(someUsers.length); // 2\n```\n\n## Mengubah *array*\n\nMari lanjutkan ke metode-metode yang mengubah dan mengatur ulang *array*.\n\n### *map*\n\nMetode [arr.map](mdn:js/Array/map) adalah salah satu metode yang paling berguna dan paling sering digunakan.\n\nMetode ini memanggil fungsi untuk tiap elemen di *array* dan mengembalikan hasilnya dalam bentuk *array*.\n\nSintaksnya yakni:\n\n```js\nlet result = arr.map(function(item, index, array) {\n  // mengembalikan nilai baru, bukan item\n});\n```\n\nSebagai contoh, di sini kita mengubah setiap elemen menjadi panjang (*length*) dari *string* elemen tersebut:\n\n```js run\nlet lengths = [\"Bilbo\", \"Gandalf\", \"Nazgul\"].map(item => item.length);\nalert(lengths); // 5,7,6\n```\n\n### *sort*(fn)\n\nPanggilan [arr.sort()](mdn:js/Array/sort) menata *array* *dalam wadah*, mengubah urutan elemen yang ada.\n\nMetode ini juga mengembalikan *array* yang sudah tertata, tetapi nilai yang dikembalikan biasanya diabaikan, mengingat `arr` itu sendiri sudah termodifikasi/diubah.\n\nContohnya:\n\n```js run\nlet arr = [ 1, 2, 15 ];\n\n// metode tersebut mengurutkan ulang konten arr\narr.sort();\n\nalert( arr );  // *!*1, 15, 2*/!*\n```\n\nApa kamu menyadari adanya keanehan dari hasil di atas?\n\nUrutannya menjadi `1, 15, 2`. Tidak benar. Tapi mengapa demikian?\n\n**_Item-item_ tersebut diurutkan sebagai *string* secara pada dasarnya.**\n\nSecara harfiah, semua elemen dikonversi menjadi *string* untuk dibandingkan. Sedangkan pada *string*, berlaku pengurutan leksikograpis dan memang benar bahwa `\"2\" > \"15\"`.\n\nUntuk menggunakan urutan penataan kita sendiri, kita perlu memberikan sebuah fungsi sebagai argumen pada `arr.sort()`.\n\nFungsi tersebut harus membandingkan dua nilai yang berubah-ubah dan mengembalikan (hasilnya):\n```js\nfunction compare(a, b) {\n  if (a > b) return 1; // if the first value is greater than the second\n  if (a == b) return 0; // if values are equal\n  if (a < b) return -1; // if the first value is less than the second\n}\n```\n\nContoh, untuk mengurutkan elemen sebagai angka:\n\n```js run\nfunction compareNumeric(a, b) {\n  if (a > b) return 1;\n  if (a == b) return 0;\n  if (a < b) return -1;\n}\n\nlet arr = [ 1, 2, 15 ];\n\n*!*\narr.sort(compareNumeric);\n*/!*\n\nalert(arr);  // *!*1, 2, 15*/!*\n```\n\nKini metode tersebu berfungsi seperti yang diinginkan.\n\nMari berhenti sejenak dan pikirkan apa yang terjadi. `arr` bisa jadi *array* berisi apapun, benar? *Array* itu bisa saja berisi angka atau *string* atau objek atau apapun. Kita memiliki sekumpulan *beberapa item*. Untuk mengurutkannya, kita perlu sebuah *fungsi pengurutan* yang tahu bagaimana cara untuk membandingkan elemen-elemen. Setelan awalnya adalah sebuah urutan *string*.\n\nMetode `arr.sort(fn)` mengimplementasikan sebuah algoritma pengurutan yang umum. Kita tidak perlu benar-benar tahu bagaimana algoritma itu bekerja (sebuah [cara cepat/*quicksort*](https://en.wikipedia.org/wiki/Quicksort) yang sudah teroptimasi sepanjang waktu). Algoritma itu akan menyusuri *array*, membandungkan elemen-elemennya menggunakan fungsi yang diberikan dan mengurutkan ulang elemen-elemen tersebut, yang perlu kita lakukan yakni memberikan `fn` yang mana akan melakukan operasi perbandingan.\n\n*Ngomong-omong*, jika kita pernah ingin tahu elemen mana saja yang dibandingkan -- cukup dengan cara memberi *alert*:\n\n```js run\n[1, -2, 15, 2, 0, 8].sort(function(a, b) {\n  alert( a + \" <> \" + b );\n  return a - b;\n});\n```\n\nAlgoritma tersebut dapat membandingkan sebuah elemen dengan banyak elemen lainnya dalam proses ini, tapi algoritma itu akan mencoba membuat sedikit mungkin perbandingan.\n\n````smart header=\"Sebuah fungsi perbandingan dapat mengembalikan angka manapun\"\nSebenarnya, sebuah fungsi perbandingan hanya perlu untuk mengembalikan sebuah angka positif untuk mengatakan \"lebih besar (dari)\" dan angka negatif untuk mengatakan \"kurang (dari)\".\n\nHal tersebut membuat penulisan fungsi jadi lebih pendek:\n\n```js run\nlet arr = [ 1, 2, 15 ];\n\narr.sort(function(a, b) { return a - b; });\n\nalert(arr);  // *!*1, 2, 15*/!*\n```\n````\n\n````smart header=\"Fungsi arrow yang terbaik\"\nIngat [fungsi arrow](info:arrow-functions-basics)? Kita dapat menggunakan fungsi arrow di sini untuk pengurutan yang lebih rapi:\n\n```js\narr.sort( (a, b) => a - b );\n```\n\nFungsi ini berfungsi samahalnya dengan metode versi yang lebih panjang di atasnya.\n````\n\n\n### *reverse*\n\nMetode [arr.reverse](mdn:js/Array/reverse) membalikkan urutan elemen di dalam `arr`.\n\nContohnya:\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\narr.reverse();\n\nalert( arr ); // 5,4,3,2,1\n```\n\nMetode ini juga mengembalikan *array* `arr` setelah proses pembalikan.\n\n### *split* dan *join*\n\nIni adalah situasi dari dunia nyata. Kita menulis sebuah aplikasi olahpesan, dan orang tersebut memasukkan ke dalam daftar penerima yang dipisahkan oleh tanda koma, antara lain: `John, Pete, Mary`. Namun bagi kita *array* yang berisi nama akan jauh lebih apik ketimbang sebuah *string*. Jadi bagaiman cara mendapatkannya?\n\nMetode [str.split(delim)](mdn:js/String/split) melakukan tepat hal yang dijelaskan di atas. Metode ini memisahkan *string* ke dalam *array* dengan *delimiter* (pemisah) `delim`.\n\nDalam contoh berikut ini, kita memisahkan elemen dengan tanda koma yang diikuti oleh spasi:\n\n```js run\nlet names = 'Bilbo, Gandalf, Nazgul';\n\nlet arr = names.split(', ');\n\nfor (let name of arr) {\n  alert( `A message to ${name}.` ); // Sebuah pesan untuk (serta nama-nama lainnya)\n}\n```\n\nMetode `split` memiliki argumen numerik oposional kedua -- sebuah batas pada panjang (*length*) *array*. Jika batasan itu diberikan, maka elemen ekstra (lebih dari batas panjang *array* yang diberikan) akan diabaikan. Dalam praktiknya, hal ini jarang digunakan:\n\n```js run\nlet arr = 'Bilbo, Gandalf, Nazgul, Saruman'.split(', ', 2);\n\nalert(arr); // Bilbo, Gandalf\n```\n\n````smart header=\"Pisah menjadi huruf\"\nPanggilan `split(s)` dengan `s` yang kosong akan memisahkan string menjadi array yang berisi huruf-huruf:\n\n```js run\nlet str = \"test\";\n\nalert( str.split('') ); // t,e,s,t\n```\n````\n\nPanggilan [arr.join(glue)](mdn:js/Array/join) melalukan pembalikan ke `split`. Panggilan ini membuat sebuah *string item-item* `arr` digabungkan oleh `glue` (lem) diantara *item* tersebut.\n\nContohnya:\n\n```js run\nlet arr = ['Bilbo', 'Gandalf', 'Nazgul'];\n\nlet str = arr.join(';'); // menempelkan/me-lem isi array menjadi sebuah string menggunakan ;\n\nalert( str ); // Bilbo;Gandalf;Nazgul\n```\n\n### *reduce*/reduceRight\n\nKetika kita perlu mengulang-ulang sebuah *array* -- kita dapat menggunakan `forEach`, `for` atau `for..of`.\n\nKetika kita perlu mengulang dan mengembalikan data setiap elemen -- kita menggunakan `map`.\n\nMetode [arr.reduce](mdn:js/Array/reduce) dan metode [arr.reduceRight](mdn:js/Array/reduceRight) juga termasuk ke dalam kelompok metode-metode tadi, tapi ada sedikit lebih rumit. Kedua metode ini digunakan untuk menghitung sebuah nilai tunggal berdasarkan *array*.\n\nSintaksnya yakni:\n\n```js\nlet value = arr.reduce(function(accumulator, item, index, array) {\n  // ...\n}, [initial]);\n```\n\nFungsi di atas diterapkan pada semua elemen *array* satu sama lainnya dan \"melanjutkan\" hasil perhitungannya ke panggilan berikutnya.\n\nArgument-argumennya yakni:\n\n- `previousValue` -- adalah hasil dari dari pemanggilan fungsi sebelumnya, sama dengan `initial` pertama kalinya (jika `initial` diberikan).\n- `item` -- adalah *item array* yang sekarang.\n- `index` -- adalah posisi *item* tersebut.\n- `array` -- adalah *array*-nya.\n\nJika fungsi sudah diterapkan, masil dari panggilan fungsi sebelumnya dioper ke panggilan selanjutnya sebagai argumen pertama.\n\nMemang terdengar rumit, tapi tidak seperti yang kamu pikirkan tentang argumen pertama sebagai \"akumulator\" yang menyimpan dan menggabungkan jasil dari semua eksekusi sebelumnya. Serta pada akhirnya, itu menjadi hasil dari `reduce`.\n\nCara termudah untuk memahami metode ini adalah dengan melihat contohnya.\n\nDi sini kita mendapat jumlah dari sebuah *array* dalam satu baris:\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\nlet result = arr.reduce((sum, current) => sum + current, 0);\n\nalert(result); // 15\n```\n\nFungsi yang dioper ke `reduce` hanya menggunakan 2 argumen,  itu cukup khas.\n\nMari lihat rincian dari apa yang terjadi.\n\n1. Pada *run* pertama, `sum` adalah nilai `initial` (argumen terakhir dari `reduce`), sama dengan `0`, dan `current` adalah elemen pertama *array* tersebut, yang sama dengan `1`. Jadi hasil fungsi tersebut adalah `1`.\n2. Pada *run* ke-dua, `sum = 1`, kita tambahkan elemen kedua dari *array* (`2`) dan mengembalikan hasilnya.\n3. Pada *run* ke-tiga, `sum = 3` dan kita menambahkan satu elemen lagi, dan seterusnya...\n\nAlur perhitungannya:\n\n![](reduce.svg)\n\nATau dalam bentuk sebuah tabel, dimana setiap baris merepresentasikan sebuah panggilan fungsi pada elemen *array* selanjutnya:\n\n|   |`sum`|`current`|result|\n|---|-----|---------|---------|\n|the first call|`0`|`1`|`1`|\n|the second call|`1`|`2`|`3`|\n|the third call|`3`|`3`|`6`|\n|the fourth call|`6`|`4`|`10`|\n|the fifth call|`10`|`5`|`15`|\n\nDi sini kita bisa dengan jelas melihat bagaimana hasil dari panggilan sebelumnya menjadi argumen pertama pada panggilan berikutnya.\n\nKita bisa juga mengjilangkan nilai awal (*initial*):\n\n```js run\nlet arr = [1, 2, 3, 4, 5];\n\n// menghilangkan nilai awal dari reduce (bukan 0)\nlet result = arr.reduce((sum, current) => sum + current);\n\nalert( result ); // 15\n```\n\nHasilnya sama. Itu karena tidak ada nilai awal, maka `reduce` mengambil elemen pertama *array* sebagai nilai awal dan memulai pengulangan dari elemen ke-dua.\n\nTabel perhitungan tersebut sama dengan yang di atas, tanpa baris pertama.\n\nAkan tetapi, penggunaan yang demikian membutuhkan perhatian lebih. Jika *array* kosong, maka panggilan `reduce` tanpa nilai awal akan menghasilkan error.\n\nBerikut ini contohnya:\n\n```js run\nlet arr = [];\n\n// Error: Reduce dari array kosong tanpa nilai awal\n// jika nilai awalnya ada, reduce akan mengembalikannya pada  arr yang kosong.\narr.reduce((sum, current) => sum + current);\n```\n\n\nJadi sangat disarankan untuk selalu menspesifikasikan nilai awal.\n\nMetode [arr.reduceRight](mdn:js/Array/reduceRight) melaksanakan hal yang sama, tapi beroperasi dari kanan ke kiri.\n\n\n## Array.isArray\n\n*Array* tidak membentuk sebuah bahasa tipe sendiri. *Array* terbentuk berdasarkan objek.\n\nJadi `typeof` tidak membantu membedakan sebuah objek polos dari sebuah *array*:\n\n```js run\nalert(typeof {}); // objek\nalert(typeof []); // sama\n```\n\n...Tetapi *array* serung diguakan hingga ada metode khusus untuk hal ini: [Array.isArray(value)](mdn:js/Array/isArray). Metode ini mengembalikan `true` jika `value` adalah sebuah *array*, dan `false` jika sebaliknya.\n\n```js run\nalert(Array.isArray({})); // false\n\nalert(Array.isArray([])); // true\n```\n\n## Kebanyakan metode mendukung \"thisArg\"\n\nHamppir semua metode *array* yang memanggil fungsi -- seperti `find`, `filter`, `map`, dengan pengecualian `sort`, menerima paramater tambahan yakni `thisArg`.\n\nParameter itu tidak dijelaskan pada bab sebelumnya, karena jarang digunakan. Tetapi demi kelengkapan kita harus menutupi metodenya.\n\nBerikut ini adalah sintkas lengkap dari metode-metode tersebut:\n\n```js\narr.find(func, thisArg);\narr.filter(func, thisArg);\narr.map(func, thisArg);\n// ...\n// thisArg adalah argumen akhir yang oposional\n```\n\nNilai dari paramater `thisArg` menjadi `this` untuk `func`.\n\nContohnya, di sini kita menggunakan metode dengan objek `army` sebagai penyaringnya, dan `thisArg` mengoper konteksnya:\n\n```js run\nlet army = {\n  minAge: 18,\n  maxAge: 27,\n  canJoin(user) {\n    return user.age >= this.minAge && user.age < this.maxAge;\n  }\n};\n\nlet users = [\n  {age: 16},\n  {age: 20},\n  {age: 23},\n  {age: 30}\n];\n\n*!*\n// cari user, dari army.canJoin yang megembalikan true\nlet soldiers = users.filter(army.canJoin, army);\n*/!*\n\nalert(soldiers.length); // 2\nalert(soldiers[0].age); // 20\nalert(soldiers[1].age); // 23\n```\n\nJika dalam contoh di atas kita menggunakan `users.filter(army.canJoin)`, maka `army.canJoin` akan bisa dipanggil sebagai fungsi yang berdisi sendiri, dengan `this=undefined`, itu smeua berujung pada error seketika.\n\nSebuah panggilan ke `users.filter(army.canJoin, army)` bisa diganti dengan `users.filter(user => army.canJoin(user))`, yang mana melakukan hal yang sama. Metode yang pertama (`users.filter(army.canJoin, army)`) lebih sering digunakan, karena sedikit lebih mudah dimengerti oleh banyak orang.\n\n## Ringkasan\n\n*Cheat sheet* tentang metode-metode *array*:\n\n- Untuk menambah/menghilangkan elemen:\n  - `push(...items)` -- menambah *item* ke bagian akhir,\n  - `pop()` -- mengekstrak sebuah *item* dari bagian akhir,\n  - `shift()` -- mengekstrak sebuah *item* dari bagian awal,\n  - `unshift(...items)` -- menambah *item* ke bagian awal.\n  - `splice(pos, deleteCount, ...items)` -- pada indeks `pos` menghapus elemen `deleteCount` dam memasukkan `items`.\n  - `slice(start, end)` -- membuat *array* baru, menyalin elemen dari posisi `start` hingga `end` (tidak inklusif) ke dalam *array* baru tersebut.\n  - `concat(...items)` --mengembalikan sebuah *array* baru: menyalin semua anggota *array* yang sekarang dan menambahkan `items` ke dalamnya. Jika `items` adalah sebuah *array*, maka elemennya yang akan diambil.\n\n- Untuk mencari di antara elemen-elemen:\n  - `indexOf/lastIndexOf(item, pos)` -- mencari `item` mulai dari posisi `pos`, mengembalikan indeksnya atau `-1` jika tidak ditemukan.\n  - `includes(value)` -- mengembalikan `true` jika *array* memiliki `value`, jika tidak akan mengembalikan `false`.\n  - `find/filter(func)` -- menyaring elemen dengan menggunakan fungsi, mengembalikan nilai awal/semua nilai yang membuat hasil *return*-nya menjadi `true`.\n  - `findIndex` seperti `find`, namun mengembalikan indeks bukan nilai.\n\n- Untuk mengulang elemen:\n  - `forEach(func)` -- memanggil `func` untuk setiap elemen, tidak mengembalikan apapun.\n\n- Untuk mengubah *array*:\n  - `map(func)` -- membuat sebuah *array* dari hasil pemanggilan `func` untuk setiap elemen.\n  - `sort(func)` -- mengurutkan *array* dalam-tempatnya, lalu mengembalikan hasilnya.\n  - `reverse()` -- membalikkan *array* dalam-tempatnya, lalu mengembalikan hasilnya.\n  - `split/join` -- mengonversi sebuah *string* menjadi *array* dan sebaliknya.\n  - `reduce(func, initial)` -- menghitung sebuah nilai tunggal pada *array* dengan cara memanggil `func` untuk setiap elemen dan mengoper hasil tersebut di antara panggilan.\n\n- Sebagai tambahan:\n  - `Array.isArray(arr)` memeriksa apakah `arr` merupakan *array* atau bukan.\n\nTolong diingat bahwa metode `sort`, `reverse` dan `splice` memodifikasi *array* itu sendiri.\n\nMetode-metode ini adalah yang paling sering digunakan, mencakupi 99% kasus penggunaan. Namun masih ada beberapa metode lainnya:\n\n- [arr.some(fn)](mdn:js/Array/some)/[arr.every(fn)](mdn:js/Array/every) memeriksa *array* terseebut.\n\n  Fungsi `fn` dipanggil pada tiap elemen *array* yang serupa dengan `map`. Jika beberapa/semua hasilnya `true`, maka akan mengembalikan `true`, jika tidak maka akan mengembalikan `false`.\n\n  Metode ini berperilaku seperti operator `||` dan `&&`: jika `fn` mengembalikan nilai yang sebenarnya,` arr.some () `segera mengembalikan` true` dan berhenti melakukan iterasi pada item lainnya; jika `fn` mengembalikan nilai yang salah,` arr.every () `segera mengembalikan` false` dan juga menghentikan iterasi pada item lainnya.\n\n  Kita bisa menggunakan `every` untuk membandingkan array:\n  ```js run\n  function arraysEqual(arr1, arr2) {\n    return arr1.length === arr2.length && arr1.every((value, index) => value === arr2[index]);\n  }\n\n  alert( arraysEqual([1, 2], [1, 2])); // true\n  ```\n\n- [arr.fill(value, start, end)](mdn:js/Array/fill) -- mengisi array dengan `nilai` berulang dari indeks` mulai` hingga `akhir`.\n\n- [arr.copyWithin(target, start, end)](mdn:js/Array/copyWithin) -- menyalin elemen dari posisi `start` hingga posisi `end` ke dalam *array itu sendiri*, pada posisi `target` (menumpuk/*overwrite* elemen yang ada).\n\n- [arr.flat(depth)](mdn:js/Array/flat)/[arr.flatMap(fn)](mdn:js/Array/flatMap) buat array baru dari array multidimensi.\n\nUntuk daftar lengkapnya, lihat [manual](mdn:js/Array).\n\nSejak pandangan pertama mungkin terlihat ada begitu banyak metode, cukup sulit untuk diingat. Namun sebenarnya hal itu jauh lebih mudah.\n\nLihat *cheat sheet* hanya untuk sekedar tahu semua metode tersebut. Lalu selesaikan *task* bab ini sebagai latihan, jadi kamu memiliki pengalaman mengenai metode *array*.\n\nSesudah itu kapan pun kamu perlu melakukan sesuatu dengan *array*, dan tidak tahu bagaimana caranya -- datanglah ke halaman ini, lihat *cheat sheet* dan temukan metode yang tepat. Contoh-contoh yang diberikan akan membantumu dalam penulisan sintaks yang benar. Setelah itu kamu akan secara otomatis mengingat metode-metode tersebut, tanpa usaha yang terlalu rumit.\n"
  },
  {
    "path": "1-js/05-data-types/06-iterable/article.md",
    "content": "\n# Iterables / Bisa di iterasi\n\nObjek yang *bisa di iterasi* adalah sebuah generalisasi dari sebuah array. Konsep itu membolehkan kita untuk membuat objek apapun yang bisa digunakan didalam perulangan `for..of`.\n\nTentu saja, Array bisa di iterasi. Tapi disana terdapat objek bawaan (built-in objek) lainnya, yang tentu saja bisa di iterasi. Contoh string juga bisa di iterasi.\n\nJika sebuah objek secara teknis bukan sebuah array, tapi representasi dari sebuah koleksi (list, set) dari sesuatu, lalu `for..of` adalah sintaks yang bagus untuk melakukan perulangan didalamnya, Jadi ayo kita lihat bagaimana cara membuat itu bekerja.\n\n\n## Symbol.iterator\n\nKita bisa dengan mudah mendapatkan konsep dari iterasi dengan membuatnya sendiri.\n\nUntuk contoh, kita mempunyai sebuah objek yang bukanlah array, tapi cocok untuk `for..of`.\n\nSeperti objek `range` yang merepresentasikan sebuah interval dari angka:\n\n```js\nlet range = {\n  from: 1,\n  to: 5\n};\n\n// Kita ingin membuat for..of untuk bisa digunakan:\n// for(let num of range) ... num=1,2,3,4,5\n```\n\nUntuk bisa membuat `range` bisa diiterasi (dan membuat `for..of` bekerja) kita harus menambahkan sebuah metode kedalam objeknya bernama `Symbol.iterator` (Simbol built-in spesian yang hanya digunakan untuk hal itu).\n\n1. Ketika `for.of` dimulai, itu akan memanggil metodenya sekali (atau error jika tidak ditemukan). Metodenya haruslah mengembalikan sebuah *iterator* -- sebuah objek dengan metode `next`.\n2. Selanjutnya, `for..of` bekerja *hanya bila itu mengembalikan objek*.\n3. Ketika `for..of` menginginkan nilai selanjutnya, itu akan memanggil `next()` didalam objeknya.\n4. Hasil dari `next()` harus mempunyai form `{done: Boolean, value: any}`, dimana `done=true` berarti iterasinya telah selesai, sebaliknya `value` adalah nilai selanjutnya.\n\nIni adalah implementasi penuh untuk `range` dengan catatan:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5\n};\n\n// 1. panggil for..of pertama kali untuk memanggil ini\nrange[Symbol.iterator] = function() {\n\n  // ini akan mengembalikan objek iterator:\n  // 2. Selanjutnya, for..of hanya bekerja dengan iterator ini, menanyakan nilai selanjutnya\n  return {\n    current: this.from,\n    last: this.to,      \n\n    // 3. next() dipanggil untuk setiap iterasi oleh perulangan for..of\n    next() {\n      // 4. itu harus mengembalikan nilai sebagai sebuah objek {done:.., nilai:...}\n      if (this.current <= this.last) {\n        return { done: false, value: this.current++ };\n      } else {\n        return { done: true };\n      }\n    }\n  };\n};\n\n// sekarang ini bekerja!\nfor (let num of range) {\n  alert(num); // 1, lalu 2, 3, 4, 5\n}\n```\n\nPerhatikan fitur utama dari *iterables*: pemisahan perhatian.\n\n- `range` sendiri tidak memiliki metode `next()`.\n- Malah, objek lainnya, yang dipanggil \"iterator\" dibuat dengan memanggil ke `range[Symbol.iterator]()`, dan `next()` miliknya menghasilkan nilai untuk diiterasi.\n\nJadi, objek iterator berbeda dari objek yang diiterasi.\n\nSecara teknis, kita mungkin menyatukannya dan menggunakan `range` nya sendiri sebagai iterator untuk membuat kode lebih simpel.\n\nSeperti ini:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  [Symbol.iterator]() {\n    this.current = this.from;\n    return this;\n  },\n\n  next() {\n    if (this.current <= this.to) {\n      return { done: false, value: this.current++ };\n    } else {\n      return { done: true };\n    }\n  }\n};\n\nfor (let num of range) {\n  alert(num); // 1, lalu 2, 3, 4, 5\n}\n```\n\nSekarang `range[Symbol.iterator]()` mengembalikan objek `range`nya sendiri: itu membutuhkan metode `next()` dan mengingat progress iterasi saat ini didalam `this.current`. Lebih pendek? Ya. Dan terkadang bagus juga.\n\nKekurangannya adalah sekarang menjadi mustahil untuk memiliki dua perulangan `for..of` yang berjalan didalam objeknya secara bersamaan: mereka akan membagi bagian-bagian iterasi, karena hanya terdapat satu iterator -- objeknya sendiri. Tapi menggunakan dua for-of adalah hal yang jarang terjadi, bahkan didalam asinkron sekalipun.\n\n```smart header=\"Iterator tak terbatas/infinite iterator\"\nInterator tak terbatas bisa dilakukan. Contoh, `range` menjadi tak terbatas terhadap `range.to = Infinity`. Atau kita bisa membuat objek yang bisa di iterasi dan menghasilkan rentetan tak terbatas dari angka yang acak. Juga bisa berguna.\n\nTidak ada batasan didalam `next`, itu bisa mengembalikan semakin banyak nilai, itu adalah hal yang normal.\n\nTentu saja, perulangan `for..of` didalam iterasi seperti itu takan ada habisnya. Tapi kita selalu bisa menghentikannya dengan menggunakan `break`.\n```\n\n\n## String bisa di iterasi\n\nArray dan string adalah dua hal yang paling banyak menggunakan iterasi.\n\nUntuk string, perulangan `for..of` akan mengiterasi karakternya:\n\n```js run\nfor (let char of \"test\") {\n  // berjalan 4 kali: sekali tiap karakter\n  alert( char ); // t, lalu e, lalu s, lalu t\n}\n```\n\nDan itu akan berjalan lancar dengan karakter pengganti (surrogate pairs)!\n\n```js run\nlet str = '𝒳😂';\nfor (let char of str) {\n    alert( char ); // 𝒳, dan lalu 😂\n}\n```\n\n## Memanggil sebuah iterator secara jelas\n\nUntuk pemahaman lebih dalam, kita lihat bagaimana untuk menggunakan sebuah iterator secara eksplisit.\n\nKita akan mengiterasi didalam sebuah string dengan cara yang sama seperti `for..of`, tapi dengan pemanggilan yang langsung. Kode ini membuat sebuah iterator string dan mendapatkan nilai dari itu secara \"manual\":\n\n```js run\nlet str = \"Hello\";\n\n// melakukan hal yang sama dengan\n// for (let char of str) alert(char);\n\n*!*\nlet iterator = str[Symbol.iterator]();\n*/!*\n\nwhile (true) {\n  let result = iterator.next();\n  if (result.done) break;\n  alert(result.value); // karakter keluar satu demi satu\n}\n```\n\nHal itu sangat jarang dibutuhkan, tapi akan memberikan kita kontrol lebih terhadap prosesnya daripada `for..of`. Contoh, kita bisa membagi proses iterasi: iterasi sedikit, lalu berhenti, lakukan hal lain, dan lalu lanjutkan nanti.\n\n## Bisa di iterasi dan seperti array [#array-like]\n\nTerdapat dua istilah resmi yang terlihat mirip, akan tetapi sangat berbeda. Perhatikan mereka baik-baik dan pahamilah untuk terhindar dari kebingungan.\n\n- *Iterables/bisa di iterasi* adalah objek yang mengimplementasikan metode `Symbol.iterator`, seperti yang dideskripsikan diatas.\n- *Array-likes/Seperti array* adalah objek yang memiliki indeks dan `length`, jadi mereka terlihat seperti array.\n\nketika kita menggunakan javascript untuk melakukan prakter didalam browser atau lingkungan pengembangan lainnya, kita mungkin bertemu objek yang bisa diiterasi atau yang seperti array, atau keduanya.\n\nContoh, string adalah keduanya, bisa diiterasi (`for..of` dapat bekerja) dan seperti array(mempunyai indeks angka dan `length`(panjang)).\n\nAkan tetapi bisa diiterasi mungkin bukanlah array. Dan sebaliknya sebuah array mungkin tidak bisa diiterasi.\n\nContoh, `range` di contoh diatas bisa diiterasi, tapi tidak seperti array, karena itu tidak memiliki properti indeks dan `length`.\n\nDan disini objek yang seperti array, tapi tidak bisa diiterasi:\n\n```js run\nlet arrayLike = { // punya indeks dan panjang(length) => seperti array\n  0: \"Hello\",\n  1: \"World\",\n  length: 2\n};\n\n*!*\n// Error (no Symbol.iterator)\nfor (let item of arrayLike) {}\n*/!*\n```\n\nContoh diatas bisa diiterasi dan seperti array yang biasanya *bukan array*, mereka tidak punya `push`, `pop`, dll. Hal seperti itu bisa merepotkan jika kita memiliki sebuah objek dan ingin bekerja dengannya sama seperti sebuah array. Misalnya, kita ingin bekerja dengan `range` menggunakan metode array. Bagaimana cara mencapai hal itu?\n\n## Array.from\n\nTerdapat sebuah metode universal [Array.from](mdn:js/Array/from) yang menerima hal yang bisa diiterasi atau nilai yang seperti array dan membuat `Array` \"sungguhan darinya. Lalu kita bisa memanggil metode array didalamnya.\n\nContoh:\n\n```js run\nlet arrayLike = {\n  0: \"Hello\",\n  1: \"World\",\n  length: 2\n};\n\n*!*\nlet arr = Array.from(arrayLike); // (*)\n*/!*\nalert(arr.pop()); // World (metode bekerja)\n```\n\n`Array.from` pada baris `(*)` menerima objeknya, memeriksanya apakah itu sesuatu yang bisa diiterasi atau seperti array, lalu membuat array bari dan menyalin seluruh item kedalamnya.\n\nHal yang serupa terjadi untuk sesuatu yang bisa diiterasi:\n\n```js\n// asumsikan bahwa range diambil dari contoh diatas\nlet arr = Array.from(range);\nalert(arr); // 1,2,3,4,5 (konversi array toString bekerja)\n```\n\nSintaks penuh dari `Array.from` juga memperbolehkan kita untuk menyediakan fungsi \"mapping\" opsional:\n```js\nArray.from(obj[, mapFn, thisArg])\n```\n\nArgumen kedua yang opsional `mapFn` bisa saja sebuah fungsi yang akan digunakan untuk setiap elemen sebelum ditambahkan kedalam array, dan `thisArg` memperbolehkan kita untuk menggunakan `this` didalamnya.\n\nContoh:\n\n```js\n// asumsikan bahwa range diambil dari contoh diatas\n\n// kuadratkan setiap angka\nlet arr = Array.from(range, num => num * num);\n\nalert(arr); // 1,4,9,16,25\n```\n\nDisini kita gunakan `Array.from` untuk mengubah string menjadi array dari karakter-karakter:\n\n```js run\nlet str = '𝒳😂';\n\n// pisahkan str menjadi array dari karakter-karakter\nlet chars = Array.from(str);\n\nalert(chars[0]); // 𝒳\nalert(chars[1]); // 😂\nalert(chars.length); // 2\n```\n\nTidak seperti `str.split`, itu bergantung pada sifat bisa diiterasi dari string dan juga, sama seperti `for..of`, yang bekerja dengan benar bahkan dengan karakter pengganti (surrogate pairs).\n\n\nSecara teknis disini itu melakukan hal yang sama seperti:\n\n```js run\nlet str = '𝒳😂';\n\nlet chars = []; // Array.from secara internal melakukan perulangan yang sama\nfor (let char of str) {\n  chars.push(char);\n}\n\nalert(chars);\n```\n\n...Tapi ini lebih pendek.  \n\nKita bahkan bisa membangun `slice` pengganti didalamnya:\n\n```js run\nfunction slice(str, start, end) {\n  return Array.from(str).slice(start, end).join('');\n}\n\nlet str = '𝒳😂𩷶';\n\nalert( slice(str, 1, 3) ); // 😂𩷶\n\n// metode natif tidak mendukung karakter pengganti\nalert( str.slice(1, 3) ); // tidak berguna (dua bagian dari karakter pengganti yang berbeda)\n```\n\n\n## Ringkasan\n\nObjek yang bisa digunakan didalam `for..if` dipanggil dengan *iterable*.\n\n- Secara teknis, iterables harus mengimplementasi nama metode `Symbol.iterator`.\n    - Hasil dari `obj[Symbol.iterator]` dipanggil dengan sebuah *iterator*. Itu menangani proses iterasi lebih jauh.\n    - Sebuah iterator harus mempunyai nama metode `next()` yang mengembalikan sebuah objek `{done: Boolean, value: any}`, disini `done:true` menandakan akhir dari proses iterasi, sebaliknya `value` adalah nilai selanjutnya.\n- Metode `Symbol.iterator` dipanggil secara otomatis oleh `for..of`, tapi kita bisa melakukannya secara langsung.\n- Iterables bawaan seperti string atau array, juga mengimplementasikan `Symbol.iterator`.\n- Iterator string tahu tentang karakter pengganti (surrogate pairs).\n\n\nObjek yang mempunyai properti indeks dan `length` dipanggil dengan *seperti-array/array-like*. Objek seperti itu mungkin mempunyai properti dan metode lainnya, tapi tidak memiliki metode bawaan dari array.\n\nJika kita melihat kedalam spesifikasinya -- kita akan melihat kebanyakan metode bawaan yang mengasumsikan bahwa mereka bekerja dengan iterables atau seperti-array daripada dengan array \"sungguhan\", karena hal itu lebih abstrak.\n\n`Array.from(obj[, mapFn, thisArg])` membuak `Array` sungguhan dari sebuah iterable atau seperti-array `obj`, dan lalu kita bisa menggunakan metode array didalamnya. Argumen opsional `mapFn` dan `thisArg` memperbolehan kita untuk menerapkan sebuah fungsi kedalam setiap item.\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/_js.view/solution.js",
    "content": "function unique(arr) {\n  return Array.from(new Set(arr));\n}\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/_js.view/test.js",
    "content": "describe(\"unique\", function() {\n  it(\"removes non-unique elements\", function() {\n    let strings = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n      \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n    ];\n\n    assert.deepEqual(unique(strings), [\"Hare\", \"Krishna\", \":-O\"]);\n  });\n\n  it(\"does not change the source array\", function() {\n    let strings = [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"];\n    unique(strings);\n    assert.deepEqual(strings, [\"Krishna\", \"Krishna\", \"Hare\", \"Hare\"]);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/07-map-set/01-array-unique-map/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Memfilter item array yang unik\n\nAnggaplah `arr` sebagai sebuah array.\n\nCiptakanlah fungsi `unique(arr)` yang harus mengembalikan array yang berisi nilai-nilai unik dari `arr`.\n\nSebagai contoh:\n\n```js\nfunction unique(arr) {\n  /* kodemu */\n}\n\nlet values = [\"Hare\", \"Krishna\", \"Hare\", \"Krishna\",\n  \"Krishna\", \"Krishna\", \"Hare\", \"Hare\", \":-O\"\n];\n\nalert( unique(values) ); // Hare, Krishna, :-O\n```\n\nP.S. Disini string dipakai sebagai contoh, tetapi nilai dengan tipe apa saja bisa dipakai.\n\nP.P.S. Pakailah `Set` untuk menyimpan nilai-nilai yang unik.\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/_js.view/solution.js",
    "content": "\nfunction aclean(arr) {\n  let map = new Map();\n\n  for(let word of arr) {\n    let sorted = word.toLowerCase().split(\"\").sort().join(\"\");\n    map.set(sorted, word);\n  }\n\n  return Array.from(map.values());\n}"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/_js.view/test.js",
    "content": "function intersection(arr1, arr2) {\n  return arr1.filter(item => arr2.includes(item));\n}\n\ndescribe(\"aclean\", function() {\n\n  it(\"returns exactly 1 word from each anagram set\", function() {\n    let arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\n    let result = aclean(arr);\n    assert.equal(result.length, 3);\n\n    assert.equal(intersection(result, [\"nap\", \"PAN\"]).length, 1);\n    assert.equal(intersection(result, [\"teachers\", \"cheaters\", \"hectares\"]).length, 1);\n    assert.equal(intersection(result, [\"ear\", \"era\"]).length, 1);\n\n  });\n\n  it(\"is case-insensitive\", function() {\n    let arr = [\"era\", \"EAR\"];\n    assert.equal(aclean(arr).length, 1);\n  });\n\n});"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/solution.md",
    "content": "Untuk menemukan semua anagram, mari kita pecahkan setiap kata menjadi huruf-huruf dan urutkanlah. Ketika huruf-huruf terurut, semua anagram adalah sama. \n\nSebagai contoh:\n\n```\nnap, pan -> anp\near, era, are -> aer\ncheaters, hectares, teachers -> aceehrst\n...\n```\n\nKita akan menggunakan varian yang diurutkan berdasarkan huruf sebagai kunci map untuk menyimpan hanya satu nilai untuk setiap kunci:\n\n```js run\nfunction aclean(arr) {\n  let map = new Map();\n\n  for (let word of arr) {\n    // split the word by letters, sort them and join back\n*!*\n    let sorted = word.toLowerCase().split('').sort().join(''); // (*)\n*/!*\n    map.set(sorted, word);\n  }\n\n  return Array.from(map.values());\n}\n\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) );\n```\n\nPenyortiran huruf dilakukan oleh deretan panggilan di baris `(*)`.\n\nUntuk kenyamanan marilah kita pecahkan menjadi beberapa baris:\n\n```js\nlet sorted = word // PAN\n  .toLowerCase() // pan\n  .split('') // ['p','a','n']\n  .sort() // ['a','n','p']\n  .join(''); // anp\n```\n\nDua kata berbeda `'PAN'` dan `'nap'` mendapatkan form urutan huruf yang sama `'anp'`.\n\nBaris berikutnya menempatkan kata tersebut ke dalam map:\n\n```js\nmap.set(sorted, word);\n```\n\nJika kita pernah bertemu kata dengan urutan huruf yang sama lagi, maka kata itu akan menggantikan nilai sebelumnya dengan kunci yang sama di dalam map. Maka dari itu kita akan selalu mempunyai maksimum satu kata untuk setiap form huruf.\n\nAkhirnya `Array.from(map.values())` mengambil iterable atas nilai-nilai map (kita tidak memperlukan kunci-kunci dalam hasilnya) dan mengembalikan array dengan isi tersebut.\n\nDisini kita juga bisa menggunakan objek biasa daripada `Map`, karena kunci adalah string.\n\nSolusinya bisa terlihat seperti ini:\n\n```js run demo\nfunction aclean(arr) {\n  let obj = {};\n\n  for (let i = 0; i < arr.length; i++) {\n    let sorted = arr[i].toLowerCase().split(\"\").sort().join(\"\");\n    obj[sorted] = arr[i];\n  }\n\n  return Object.values(obj);\n}\n\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) );\n```\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/02-filter-anagrams/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Filter anagram\n\n[Anagram](https://en.wikipedia.org/wiki/Anagram) adalah kata-kata yang mempunyai jumlah huruf-huruf yang sama, tetapi dengan susunan yang berbeda. \n\nSebagai contoh:\n\n```\nnap - pan\near - are - era\ncheaters - hectares - teachers\n```\n\nCiptakanlah fungsi `aclean(arr)` yang mengembalikan array yang bersih dari anagram.\n\nSebagai contoh:\n\n```js\nlet arr = [\"nap\", \"teachers\", \"cheaters\", \"PAN\", \"ear\", \"era\", \"hectares\"];\n\nalert( aclean(arr) ); // \"nap,teachers,ear\" or \"PAN,cheaters,era\"\n```\n\nDari setiap grup anagram hanya harus tersisa satu kata, boleh yang mana saja.\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/03-iterable-keys/solution.md",
    "content": "\nItu karena `map.keys()`  mengembalikan iterable, tetapi bukan sebuah array.\n\nKita bisa mengubahnya ke sebuah array menggunakan `Array.from`:\n\n```js run\nlet map = new Map();\n\nmap.set(\"name\", \"John\");\n\n*!*\nlet keys = Array.from(map.keys());\n*/!*\n\nkeys.push(\"more\");\n\nalert(keys); // name, more\n```\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/03-iterable-keys/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Kunci-kunci iterable\n\nKami ingin mendapatkan array daripada `map.keys()` dalam satu variabel lalu mengaplikasikan metode yang array spesifik kepadanya, contoh `.push`.\n\nTapi itu tidak berhasil:\n\n```js run\nlet map = new Map();\n\nmap.set(\"name\", \"John\");\n\nlet keys = map.keys();\n\n*!*\n// Error: keys.push is not a function\nkeys.push(\"more\");\n*/!*\n```\n\nMengapa? Bagaimana kita bisa membenarkan kode ini untuk membuat `keys.push` berhasil?\n"
  },
  {
    "path": "1-js/05-data-types/07-map-set/article.md",
    "content": "\n# Map dan Set\n\nSekarang kita telah membelajari struktur data compleks berikut:\n\n- Objek untuk menyimpan koleksi kunci.\n- Array untuk menyimpan koleksi berurut.\n\nTapi itu tidak cukup dalam kehidupan nyata. Itu sebabnya `Map` dan` Set` juga ada.\n\n## Map\n\n[Map](mdn:js/Map) adalah kumpulan item data yang berkunci, seperti `Object`. Tetapi perbedaan utama adalah `Map` membolehkan kunci jenis apa pun.\n\nMetode dan properti:\n\n- `new Map()` -- menciptakan map.\n- `map.set(key, value)` -- menyimpan nilai dengan kunci.\n- `map.get(key)` -- mengembalikan nilai dengan kunci, `undefined` jika` key` tidak ada di map.\n- `map.has(key)` -- mengembalikan `true` jika` key` ada, `false` sebaliknya.\n- `map.delete(key)` -- menghapus nilai dengan kunci.\n- `map.clear()` -- menghapus semua isi dari map.\n- `map.size` -- mengembalikan jumlah elemen saat ini.\n\nMisalnya:\n\n```js run\nlet map = new Map();\n\nmap.set('1', 'str1');   // kunci string\nmap.set(1, 'num1');     // kunci nomor\nmap.set(true, 'bool1'); // kunci boolean\n\n//ingat Object biasa? ia akan mengkonversi kunci menjadi string\n//Map menyimpan tipenya, jadi kedua berikut tidaklah sama:\nalert( map.get(1)   ); // 'num1'\nalert( map.get('1') ); // 'str1'\n\nalert( map.size ); // 3\n```\n\nSeperti yang dapat kita lihat, lain dari objek, kunci tidak dikonversi ke string. Jenis kunci apa pun dimungkinkan.\n\n```smart header=\"`map[key]` bukan cara yang baik untuk menggunakan `Map`\"\nMeski `map[key]` juga bekerja, misal kita bisa mengeset `map[key] = 2`, ini memperlakukan `map` sebagai objek JavaScript biasa, berimplikasi pada semua limitasi yang sesuai (tak ada kunci objek dan lain-lain).\n\nJadi kita sebaiknya memakai metode `map`: `set`, `get` dan seterusnya.\n```\n\n**Map juga dapat menggunakan objek sebagai kunci.**\n\nMisalnya:\n\n```js run\nlet john = { name: \"John\" };\n\n// Untuk setiap pengguna, mari kita simpan jumlah kunjungan mereka\nlet visitsCountMap = new Map();\n\n// john adalah kunci bagi mapnya\nvisitsCountMap.set(john, 123);\n\nalert( visitsCountMap.get(john) ); // 123\n```\n\nMenggunakan objek sebagai kunci adalah salah satu fitur `Map` yang paling terkenal dan penting. Untuk kunci string, `Object` bisa dipakai, tetapi tidak untuk kunci objek.\n\nMari kita coba:\n\n```js run\nlet john = { name: \"John\" };\nlet ben = { name: \"Ben\" };\n\nlet visitsCountObj = {}; // cobalah memakai objek\n\nvisitsCountObj[ben] = 234; // cobalah memakai ben sebagai kunci\nvisitsCountObj[john] = 123; // cobalah memakai john sebagai kunci\n\n*!*\n// Inilah yang tertulis!\nalert( visitsCountObj[\"[object Object]\"] ); // 123\n*/!*\n```\n\nKarena `visitsCountObj` adalah sebuah objek, ia mengubah semua kunci, seperti `john` menjadi string, jadi kita mendapatkan kunci string `\"[object Object]\"`. Jelas bukan yang kita inginkan.\n\n```smart header=\"Cara `Map` membandingkan kunci\"\nUntuk mengetes kesamaan kunci, `Map` menggunakan algoritma [SameValueZero](https://tc39.github.io/ecma262/#sec-samevaluezero). Ini kira-kira sama dengan kesetaraan ketat `===`, tetapi perbedaannya adalah `NaN` dianggap sama dengan `NaN`. Jadi `NaN` bisa digunakan sebagai kunci juga.\n\nAlgoritma ini tidak dapat diubah atau dikustomisasi.\n```\n\n````smart header=\"Chaining\"\nSetiap panggilan `map.set` mengembalikan map itu sendiri, sehingga kami dapat \"mem-chain\" panggilan-panggilan:\n\n```js\nmap.set('1', 'str1')\n  .set(1, 'num1')\n  .set(true, 'bool1');\n```\n````\n\n\n## Iterasi Atas Map\n\nUntuk looping atas `map`, ada 3 method:\n\n- `map.keys()` -- mengembalikan iterable untuk kunci,\n- `map.values()` -- mengembalikan iterable untuk nilai,\n- `map.entries()` -- mengembalikan iterable untuk entri `[key, value]`, ini digunakan dengan standar di `for..of`.\n\nMisalnya:\n\n```js run\nlet recipeMap = new Map([\n  ['cucumber', 500],\n  ['tomatoes', 350],\n  ['onion',    50]\n]);\n\n// iterasi atas kunci (vegetables)\nfor (let vegetable of recipeMap.keys()) {\n  alert(vegetable); // cucumber, tomatoes, onion\n}\n\n// iterasi atas nilai (amounts)\nfor (let amount of recipeMap.values()) {\n  alert(amount); // 500, 350, 50\n}\n\n// iterasi atas entri-entri [key, value] \nfor (let entry of recipeMap) { // the same as of recipeMap.entries()\n  alert(entry); // cucumber,500 (and so on)\n}\n```\n\n```smart header=\"Urutan insersi digunakan\"\nIterasi berjalan dalam urutan yang sama dengan nilai yang dimasukkan. `Map` mempertahankan urutan ini, tidak seperti `Object` biasa.\n```\n\nSelain itu, `Map` memilike method `forEach`, mirip dengan `Array`:\n\n```js\n// menjalankan fungsi untuk setiap pasangan (kunci, nilai)\nrecipeMap.forEach( (value, key, map) => {\n  alert(`${key}: ${value}`); // cucumber: 500 etc\n});\n```\n\n## Object.entries: Map dari Object\n\nKetika `Map` diciptakan, kita bisa memberi array (atau iterabel lainnya) pasangan kunci/nilai untuk inisialisasi, seperti ini:\n\n```js run\n// array berisi pasangan [kunci, nilai]\nlet map = new Map([\n  ['1',  'str1'],\n  [1,    'num1'],\n  [true, 'bool1']\n]);\n\nalert( map.get('1') ); // str1\n```\n\nJika kita memiliki objek biasa, dan kita mau menciptakan sebuah `Map` darinya, kita bisa menggunakan method built-in [Object.entries(obj)](mdn:js/Object/entries) yang mengembalikan array daripada pasangan-pasangan kunci/nilai untuk satu objek yang berformat persis sama.\n\nJadi kita bisa menciptakan map dari objek seperti ini:\n\n```js run\nlet obj = {\n  name: \"John\",\n  age: 30\n};\n\n*!*\nlet map = new Map(Object.entries(obj));\n*/!*\n\nalert( map.get('name') ); // John\n```\n\nDisini, `Object.entries` mengembalikan array daripada pasangan-pasangan kunci/nilai: `[ [\"name\",\"John\"], [\"age\", 30] ]`. Itu yang `Map` perlukan.\n\n\n## Object.fromEntries: Object dari Map\n\nKita baru saja menyaksikan cara menciptakan `Map` dari objek biasa dengan `Object.entries(obj)`.\n\nAda juga method `Object.fromEntries` yang melakukan kebalikkannya: jika diberi array berisi pasangan `[kunci, nilai]`, ia menciptakan objek darinya:\n\n```js run\nlet prices = Object.fromEntries([\n  ['banana', 1],\n  ['orange', 2],\n  ['meat', 4]\n]);\n\n// now prices = { banana: 1, orange: 2, meat: 4 }\n\nalert(prices.orange); // 2\n```\n\nKita bisa menggunakan `Object.fromEntries` untuk mendapatkan objek polos dari `Map`.\n\nContoh: Kita menyimpan data di dalam `Map`, tapi kita perlu mengirimnya ke kode pihak ketiga yang mengharapkan objek biasa.\n\nKita mulai:\n\n```js run\nlet map = new Map();\nmap.set('banana', 1);\nmap.set('orange', 2);\nmap.set('meat', 4);\n\n*!*\nlet obj = Object.fromEntries(map.entries()); // ciptakan objek biasa (*)\n*/!*\n\n// selesai!\n// obj = { banana: 1, orange: 2, meat: 4 }\n\nalert(obj.orange); // 2\n```\n\nPemanggilan `map.entries()` mengembalikan sebuah iterable dari pasangan key/value, persis didalam format dari `Object.fromEntries`.\n\nKita juga bisa membuat barisan `(*)` lebih pendek:\n```js\nlet obj = Object.fromEntries(map); // hilangkan .entries()\n```\n\nItu sama, karena `Object.fromEntries` mengharapkan objek iterabel sebagai argumen. Tidak harus sesuatu array. Dan iterasi standar untuk `map` mengembalikan pasangan kunci/nilai yang sama dengan `map.entries()`. Jadi kita mendapatkan objek biasa dengan kunci/nilai yang sama dengan `map`.\n\n## Set\n\n`Set` adalah tipe koleksi spesial - \"set nilai-nilai\" (tanpa kunci), dimana setiap nilai hanya dapat terjadi sekali.\n\nMethod utamanya adalah:\n\n- `new Set(iterable)` -- menciptakan set, dan jika objek `iterable` disediakan (biasanya array), menyalin nilai darinya ke set.\n- `set.add(value)` -- menambahkan nilai, mengembalikan set itu sendiri.\n- `set.delete(value)` -- menghapus nilai, mengembalikan `true` jika `value` ada pada saat panggilan berlangsung, jika tidak `false`.\n- `set.has(value)` -- mengembalikan `true` jika nilai ada di set, jika tidak `false`.\n- `set.clear()` -- menghapus semuanya dari set.\n- `set.size` -- adalah hitungan elemen.\n\nFitur utamanya adalah panggilan berulang `set.add(value)` dengan nilai yang sama tidak melakukan apa-apa. Itulah alasan mengapa setiap nilai hanya muncul dalam `Set` sekali.\n\nMisalnya, ada pengunjung yang datang, dan kami ingin mengingat semua orang. Tetapi kunjungan berulang tidak harus menyebabkan duplikasi. Pengunjung harus \"dihitung\" hanya sekali.\n\n`Set` adalah hal yang tepat untuk itu:\n\n```js run\nlet set = new Set();\n\nlet john = { name: \"John\" };\nlet pete = { name: \"Pete\" };\nlet mary = { name: \"Mary\" };\n\n// kunjungan-kunjungan, beberapa pengguna datang berkali-kali\nset.add(john);\nset.add(pete);\nset.add(mary);\nset.add(john);\nset.add(mary);\n\n// set hanya menyimpan nilai-nilai unik\nalert( set.size ); // 3\n\nfor (let user of set) {\n  alert(user.name); // John (lalu Pete dan Mary)\n}\n```\n\nAlternatif untuk `Set` dapat berupa array pengguna, dan kode untuk memeriksa duplikat pada setiap insersi menggunakan [arr.find](mdn:js/Array/find). Tetapi kinerjanya akan jauh lebih buruk, karena metode ini menjalani seluruh array memeriksa setiap elemen. `Set` jauh lebih baik dioptimalkan secara internal untuk pemeriksaan keunikan.\n\n## Iteration atas Set\n\nKita bisa meng-loop atas set dengan `for..of` atau menggunakan `forEach`:\n\n```js run\nlet set = new Set([\"oranges\", \"apples\", \"bananas\"]);\n\nfor (let value of set) alert(value);\n\n// sama untuk forEach:\nset.forEach((value, valueAgain, set) => {\n  alert(value);\n});\n```\n\nIngat keanehannya. Fungsi callback yang dilewatkan dalam `forEach` memiliki 3 argumen: satu `value`, kemudian *nilai yang sama* `valueAgain`, dan kemudian objek target. Memang, nilai yang sama muncul dalam argumen dua kali.\n\nItu untuk kompatibilitas dengan `Map` di mana callback yang dilewati `forEach` memiliki tiga argumen. Terlihat agak aneh, memang. Tetapi dapat membantu mengganti `Map` dengan` Set` dalam kasus-kasus tertentu dengan mudah, dan sebaliknya.\n\nMetode yang sama yang dimiliki `Map` untuk iterator juga didukung:\n\n- `set.keys()` -- mengembalikan objek iterable untuk nilai,\n- `set.values()` -- sama dengan `set.keys()`, untuk kompatibilitas dengan `Map`,\n- `set.entries()` -- mengembalikan objek iterable untuk entri `[nilai, nilai]`, ada untuk kompatibilitas dengan `Map`.\n\n## Ringkasan\n\n`Map` -- adalah kumpulan nilai-nilai berkunci.\n\nMetode dan properti:\n\n- `new Map([iterable])` -- membuat map, dengan `iterable` opsional (mis. array) dari pasangan `[key, value]` untuk inisialisasi.\n- `map.set(key, value)` -- menyimpan nilai dengan kunci.\n- `map.get(key)` -- mengembalikan nilai dengan kunci, `undefined` jika `key` tidak ada di map.\n- `map.has(key)` -- mengembalikan `true` jika `key` ada, `false` sebaliknya.\n- `map.delete(key)` -- menghapus nilai dengan kunci.\n- `map.clear()` -- menghapus semuanya dari peta.\n- `map.size` -- mengembalikan jumlah elemen saat ini.\n\nPerbedaan dari `Object` biasa:\n\n- Kunci apa saja, objek bisa dijadikan kunci.\n- Metode-metode tambahan untuk kenyamanan, properti `size`.\n\n`Set` -- adalah kumpulan nilai-nilai unik.\n\nMetode dan properti:\n\n- `new Set([iterable])` -- membuat set, dengan nilai opsional `iterable` (mis. array) untuk inisialisasi.\n- `set.add(value)` -- menambahkan nilai (tidak melakukan apa-apa jika `value` ada), mengembalikan set itu sendiri.\n- `set.delete(value)` -- menghapus nilai, mengembalikan `true` jika `value` ada pada saat panggilan berlangsung, jika tidak `false`.\n- `set.has(value)` -- mengembalikan `true` jika nilai ada di set, jika tidak `false`.\n- `set.clear()` -- menghapus semuanya dari set.\n- `set.size` -- adalah hitungan elemen.\n\nIterasi atas `Map` dan` Set` selalu dalam urutan insersi, jadi kami tidak dapat mengatakan bahwa koleksi ini tidak berurut, tetapi kami tidak dapat menyusun ulang elemen atau secara langsung mendapatkan elemen dengan nomornya.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/01-recipients-read/solution.md",
    "content": "Ayo kita simpan pesan yang dibaca didalam `WeakSet`:\n\n```js run\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n\nlet readMessages = new WeakSet();\n\n// dua pesan telah dibaca\nreadMessages.add(messages[0]);\nreadMessages.add(messages[1]);\n// readMessages mempunyai 2 elemen\n\n// ...sekarang baca pesan pertama lagi!\nreadMessages.add(messages[0]);\n// readMessages masih memiliki 2 elemen yang unik\n\n// jawaban: apakah message[0] telah dibaca?\nalert(\"Read message 0: \" + readMessages.has(messages[0])); // true\n\nmessages.shift();\n// sekarang readMessages mempunyai 1 elemen (secara teknis memory mungkin akan dibersihkan nanti)\n```\n\n`WeakSet` membolehkan untuk menyimpan satu set dari messages dan dengan mudah memeriksa apakah sebuah pesan ada didalamnya.\n\nItu akan membersihkan dirinya sendiri secara otomatis. Timbal baliknya adalah kita tidak bisa melakukan iterasi didalamnya, tidak bisa mendapatkan \"semua pesan yang telah dibaca\" darinya secara langsung. Tapi kita bisa melakukan iterasi kepada seluruh pesan dan memfilter semuanya yang ada didalam set.\n\nHal lainnya, solusi berbeda bisa saja seperti menambahkan properti seperti `message.isRead=true` kepada pesan setelah pesannya dibaca. Seperti objek pesan dikelola oleh kode lain, hal itu tidak direkomendasikan, tapi kita bisa menggunakan properti simbol untuk menghindari konflik.\n\nSeperti ini:\n```js\n// properti simbol yang hanya diketahui kode kita\nlet isRead = Symbol(\"isRead\");\nmessages[0][isRead] = true;\n```\n\nSekarang kode dari pihak-ketiga kemungkinan tidak akan melihat properti tambahan kita.\n\nwalaupun simbol membolehkan kita untuk mengecilkan kemunculan dari masalah, menggunakan `WeakSet` lebih baik dari sisi arsitektural."
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/01-recipients-read/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Menyimpan tanda \"unread\"\n\nTerdapat beberapa pesan dari array\"\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n```\n\nKode kamu bisa mengaksesnya, tapi pesannya di kelola oleh kode orang lain. Pesan baru ditambahkan, pesan lama dihilangkan secara secara teratur oleh kode itu, dan kamu tidak tahu persis saat ketika itu terjadi.\n\nSekarang, struktur data mana yang harus kamu gunakan untuk menyimpan informasi tentang pesannya apakah \"telah dibaca\"? Strukturnya haruslah tepat untuk memberikan jawaban \"apakah telah dibaca\"? untuk pesan objek yang diberikan.\n\nCatatan. Ketika sebuah pesan dihilangkan dari `messages`, pesan itu harus menghilang dari strukturnya juga.\n\nCatatan tambahan. Kita seharusnya tidak memodifikasi objek message, tambahkan properti kita kedalamnya. Seperti mereka di kelola oleh kode orang lain, itu mungkin akan mengarah ke hasil akhir yang tidak diinginkan.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/solution.md",
    "content": "\nUntuk menyimpan tanggal, kita bisa menggunakan `WeakMap`:\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n\nlet readMap = new WeakMap();\n\nreadMap.set(messages[0], new Date(2017, 1, 1));\n// Kita akan belajar objek Date nanti\n```\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/02-recipients-when-read/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Menyimpan tanggal\n\nTerdapat sebuah array dari pesan sama seperti di [previous task](info:task/recipients-read).Situasinya sama.\n\n```js\nlet messages = [\n  {text: \"Hello\", from: \"John\"},\n  {text: \"How goes?\", from: \"John\"},\n  {text: \"See you soon\", from: \"Alice\"}\n];\n```\n\nPertanyaannya: struktur data mana yang kamu gunakan untuk menyimpan informasinya: \"ketika pesannya dibaca?\".\n\nDi tugas sebelumnya kita hanya menyimpan \"yes/no\". Sekarang kita butuh untuk menyimpan tanggal, dan itu harus tetap berada di memori simpan sampai pesannya dibuang.\n\nCatatan. Tanggal bisa disimpan sebagai objek dengan kelas bawaan `Date, kita akan mempelajarinya nanti.\n"
  },
  {
    "path": "1-js/05-data-types/08-weakmap-weakset/article.md",
    "content": "# WeakMap dan WeakSet\n\nSeperti yang kita tahu dari bab <info:garbage-collection>, Mesin Javascript menyimpan sebuah nilai didalam memori selama itu bisa terjangkau (dan secara potensial bisa digunakan).\n\nContoh:\n```js\nlet john = { name: \"John\" };\n\n// Objeknya bisa diakses, john mereferensi kedalamnya.\n\n// tulis urang referensinya\njohn = null;\n\n*!*\n// objeknya akan dihilangkan dari memori\n*/!*\n```\n\nBiasanya. properti dari sebuah objek atau elemen dari array atau struktur data lainnya bisa dianggap bisa dijangkau dan tetap berada dimemori selama struktur datanya masih didalam memori.\n\nContoh, jika kita memasukan objek kedalam sebuah array, lalu selama arraynya ada, objeknya akan tetap ada juga, bahkan jika disana sudah tidaka ada yang mereferensi kedalamnya.\n\nSeperti ini:\n\n```js\nlet john = { name: \"John\" };\n\nlet array = [ john ];\n\njohn = null; // tulis ulang referensinya\n\n*!*\n\n// the object previously referenced by john is stored inside the array \n// therefore it won't be garbage-collected\n// we can get it as array[0]\n*/!*\n```\n\nSama seperti itu, jika kita bisa menggunakan sebuah objek sebagai sebuah kunci/key didalam `Map` biasa, lalu selama `Map`nya ada, objeknya akan selalu ada juga. Itu akan menempati memori dan mungkin tidak akan dibuang.\n\nContoh:\n\n```js\nlet john = { name: \"John\" };\n\nlet map = new Map();\nmap.set(john, \"...\");\n\njohn = null; // tulis ulang referensinya\n\n*!*\n// john disimpan didalam map,\n// kita bisa mendapatkannya dengan menggunakan map.keys()\n*/!*\n```\n\n`WeakMap` secara dasar berbeda didalam aspek ini. Itu tidak akan mencegah pembuangan dari objek kunci.\n\nAyo kita lihat didalam contoh.\n\n## WeakMap\n\nPerbedaan pertama dari `Map` adalah kunci `WeakMap` haruslah objek, bukan nilai primitif:\n\n```js run\nlet weakMap = new WeakMap();\n\nlet obj = {};\n\nweakMap.set(obj, \"ok\"); // bekerja dengan benar (object kunci/key)\n\n*!*\n// tidak bisa menggunakan string sebagai kunci\nweakMap.set(\"test\", \"Whoops\"); // Error, karena \"test\" bukanlah sebuah objek\n*/!*\n```\n\nSekarang, jika kita menggunakan sebuah objek sebagai kunci didalamnya, dan disana tidak terdapat referensi lain ke objeknya -- itu akan dihilangkan dari memori (dan juga dari map) secara otomatis.\n\n```js\nlet john = { name: \"John\" };\n\nlet weakMap = new WeakMap();\nweakMap.set(john, \"...\");\n\njohn = null; // tulis ulang referensinya\n\n// jogn telah dihilangkan dari memori!\n```\n\nBandingkan itu dengan `Map` biasa dicontoh diatas. Sekarang jika `john` hanya ada jika sebagai kunci dari `WeakMap` -- itu akan secara otomatis dihapus dari map (dan memori).\n\n`WeakMap` tidak mendukung iterasi dan metode `keys()`, `nilai()`, `entries()`, jadi tidak ada cara untuk mendapatkan semua kunci atau nilai darinya.\n\n`WeakMap` hanya mempunyai metode berikut:\n\n- `weakMap.get(key)`\n- `weakMap.set(key, value)`\n- `weakMap.delete(key)`\n- `weakMap.has(key)`\n\nKenapa terdapat batasan seperti itu? Itu hanyalah untuk alasan teknis. Jika sebuah objek kehilangan semua referensi lainnya (seperti `john` didalam kode diatas), lalu itu akan dibuang secara otomatis. Tapi secara teknis itu tidak benar-benar di spesifikasikan *ketika pembersihan terjadi*.\n\nMesin Javascript yang memilih hal itu. Itu mungkin akan memilih untuk melakukan pembersihan memori seketika atau menunggu dan melakukan pembersihan nanti ketika penghapusan lainnya terjadi. Jadi, secara teknis elemen count yang sekarang dari `WeakMap` tidak diketahui. Mesinnya mungkin sudah menghapusnya atau belum, atau sudah dihapus sebagian. Untuk alasan itu, metode yang mengakses seluruh key/nilai tidak didukung.\n\nSekarang dimana kita butuh struktur data seperti itu?\n\n## Kasus: tambahan data\n\nBagian utama dari penggunaan `WeakMap` adalah sebuah *penambahan penyimpanan data*.\n\nJika kita bekerja dengan sebuah objek yang \"dimiliki\" kode yang lain, bahkan mungkin sebuah librari pihak-ketiga, dan harus menyimpan beberapa data yang terkait dengannya, itu harus ada selama objeknya ada - lalu `WeakMap` adalah sesuatu yang dibutuhkan.\n\nKita menyimpan datanya kedalam `WeakMap`, menggunakan objek sebagai kunci, dan ketika objeknya dihapus, datanya akan secara otomatis menghilang juga.\n\n```js\nweakMap.set(john, \"secret documents\");\n// jika john meninggal, secret documents-nya akan dihapus secara otomatis\n```\n\nKita lihat didalam contoh.\n\nContoh, kita mempunyai kode yang menyimpan hitungan kunjungan untuk pengguna. Informasinya disimpan didalam map: sebuah objek user adalah kunci dan hitungan kunjungan adalah nilainya. Ketika pengguna pergi (objeknya akan dihapus), kita tidak ingin kunjungan mereka dihitung lagi.\n\nIni adalah contoh dari fungsi penghitung dengan `Map`:\n\n```js\n// 📁 visitsCount.js\nlet visitsCountMap = new Map(); // map: user => kunjungan dihitung\n\n// naikan hitungan kunjungan\nfunction countUser(user) {\n  let count = visitsCountMap.get(user) || 0;\n  visitsCountMap.set(user, count + 1);\n}\n```\n\nDan disini bagian kode lainnya, mungkin file lainnya akan menggunakannya:\n\n```js\n// 📁 main.js\nlet john = { name: \"John\" };\n\ncountUser(john); // hitung kunjungannya\n\n// lalu john pergi\njohn = null;\n```\n\nSekarang objek `john` harusnya dihapus, tapi tetap berada di memori, itu sebagai kunci didalam `visitsCountMap`.\n\nKita perlu membersihkan `visitsCountMap` ketika kita menghapus pengguna, sebaliknya itu akan tetap didalam memori terus-menerus. Pembersihan seperti itu akan menjadi pekerjaan yang membosankan didalam arsitektur yang rumit.\n\nMalahan kita bisa menghindarinya dengan berpindah ke `WeakMap`:\n\n```js\n// 📁 visitsCount.js\nlet visitsCountMap = new WeakMap(); // weakmap: user => kunjungan dihitung\n\n// naikan hitungan kunjungan\nfunction countUser(user) {\n  let count = visitsCountMap.get(user) || 0;\n  visitsCountMap.set(user, count + 1);\n}\n```\n\nSekarang kita tidak harus membersihkan `visitsCountMap`. Setelah objek `john` menjadi tidak terjangkau lagi kecuali sebagai kunci dari `WeakMap`, itu akan dihilangkan dari memori, bersamaan dengan informasi kuncinya dari `WeakMap`.\n\n## Kasus: penyimpanan cache\n\nContoh biasa lainnya adalah penyimpanan cache: ketika sebuah hasil dari fungsi harus diingat (\"di cache\"), jadi didalam pemanggilan selanjutnya didalam objek yang sama bisa menggunakannya.\n\nKita bisa menggunakan `Map` untuk menyimpan hasil, seperti ini:\n\n```js run\n// 📁 cache.js\nlet cache = new Map();\n\n// hitung dan ingat hasilnya\nfunction process(obj) {\n  if (!cache.has(obj)) {\n    let result = /* kalkulasi hasil */ obj;\n\n    cache.set(obj, result);\n  }\n\n  return cache.get(obj);\n}\n\n*!*\n// sekarang kita gunakan process() didalam file lainnya:\n*/!*\n\n// 📁 main.js\nlet obj = {/* katakan kita mempunyai objek */};\n\nlet result1 = process(obj); // dihitung\n\n// ...selanjutnya, dari bagian kode lainnya...\nlet result2 = process(obj); // ingat hasil yang diambil dari cache\n\n// ...nanti, ketika objek tidak dibutuhkan lagi:\nobj = null;\n\nalert(cache.size); // 1 (Ouch! Objeknya masih didalam cache, menggunakan memori)\n```\n\nUntuk banyak pemanggilan dari `process(obj)` dengan objek yang sama, itu akan mengkalkulasikan hasilnya pertama kali, dan lalu hanya mengambilnya dari `cache`. kekurangannya adalah kita perlu membersihkan `cache` ketika objeknya tidak dibutuhkan lagi.\n\nJika kita mengganti `Map` dengan `WeakMap`, kemudian masalah ini menghilang: hasil yang di cache akan dihapus dari memori secara otomatis setelah objeknya dihapus.\n\n```js run\n// 📁 cache.js\n*!*\nlet cache = new WeakMap();\n*/!*\n\n// hitung dan ingat hasilnya\nfunction process(obj) {\n  if (!cache.has(obj)) {\n    let result = /* perhitungan hasil */ obj;\n\n    cache.set(obj, result);\n  }\n\n  return cache.get(obj);\n}\n\n// 📁 main.js\nlet obj = {/* objek */};\n\nlet result1 = process(obj);\nlet result2 = process(obj);\n\n// ...nanti, ketika objeknya tidak dibutuhkan lagi:\nobj = null;\n\n// tidak bisa mendapatkan cache.size, karena itu WeakMap,\n// tapi itu 0 atau nanti akan jadi 0\n// Ketika obj dihapus, data cache akan dihapus juga\n```\n\n## WeakSet\n\n`WeakSet` memiliki perilaku yang sama:\n\n- Analoginya adalah untuk meng-`Set`, tapi mungkin kita hanya butuh menambahkan objek kedalam `WeakSet` (bukan primitif).\n- Sebuah objek ada didalam set selama itu bisa dijangkau dari tempat lain.\n- Seperti `Set`, itu mendukung `add`, `has` dan `delete`, tapi tidak `size`, `keys()` dan tidak ada iterasi\n\nmenjadi \"weak\", itu juga menyediakan penyimpanan tambahan. Tapi tidak untuk data yang asal-asalan, tapi untuk \"yes/no\". Keanggotaan dari `WeakSet` mungkin berarti sesuatu tentang objeknya.\n\nContoh, kita bisa menambahkan user kedalam `WeakSet` untuk mengetahui dari siapa saja yang mengunjungi website kita:\n\n```js run\nlet visitedSet = new WeakSet();\n\nlet john = { name: \"John\" };\nlet pete = { name: \"Pete\" };\nlet mary = { name: \"Mary\" };\n\nvisitedSet.add(john); // John mengunjungi website\nvisitedSet.add(pete); // lalu Pete\nvisitedSet.add(john); // John lagi\n\n// visitedSet sekarang memiliki 2 user\n\n// periksa jika John telah berkunjung?\nalert(visitedSet.has(john)); // true\n\n// periksa jika Mary telah berkunjung?\nalert(visitedSet.has(mary)); // false\n\njohn = null;\n\n// visitedSet akan dibersihkan secara otomatis\n```\n\nHal yang paling bisa diingat adalah batasan dari `WeakMap` dan `WeakSet` adalah tidak adanya iterasi, dan ketidak mampuan untuk mendapatkan seluruh konten saat ini. Itu mungkin akan merepotkan, tapi tidak mencegah `WeakMap/WeakSet` untuk melakukan tugas utama mereka -- menjadi \"tambahan\" penyimpanan data dari objek yang disimpan/dikelola di tempat lain.\n\n## Ringkasan\n\n`WeakMap` adalah koleksi seperti-`Map` yang mengijinkan hanya objek sebagai kunci dan menghapus mereka bersama dengan nilai yang terkait sekalinya mereka menjadi tidak terjangkau.\n\n`WeakSet` adalah koleksi seperti-`Set` yang hanya menyimpan objek dan menghapus mereka sekalinya mereka menjadi tidak bisa diakses.\n\nKeduanya tidak mendukung metode dan properti yang mengacu pada seluruh kunci atau jumlah mereka. Hanya operasi individual yang diperbolehkan.\n\n`WeakMap` dan `WeakSet` digunakan sebagai struktur data \"kedua\" sebagai tambahan kepada penyimpanan objek \"utama\". Sekalinya objeknya dihapus dari penyimpanan utama, jika itu hanya ditemukan sebagai kunci dari `WeakMap` atau didalam `WeakSet`, itu akan dihapus secara otomatis.\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/solution.js",
    "content": "function sumSalaries(salaries) {\n\n  let sum = 0;\n  for (let salary of Object.values(salaries)) {\n    sum += salary;\n  }\n\n  return sum;\n}\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/_js.view/test.js",
    "content": "describe(\"sumSalaries\", function() {\n  it(\"returns sum of salaries\", function() {\n    let salaries = {\n      \"John\": 100,\n      \"Pete\": 300,\n      \"Mary\": 250\n    };\n\n    assert.equal( sumSalaries(salaries), 650 );\n  });\n\n  it(\"returns 0 for the empty object\", function() {\n    assert.strictEqual( sumSalaries({}), 0);\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/solution.md",
    "content": "```js run demo\nfunction sumSalaries(salaries) {\n\n  let sum = 0;\n  for (let salary of Object.values(salaries)) {\n    sum += salary;\n  }\n\n  return sum; // 650\n}\n\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n\nalert( sumSalaries(salaries) ); // 650\n```\nAtau kita juga bisa mendapatkan jumlah total dengan menggunakan `Object.values` dan `reduce`:\n\n```js\n// reduce meng-loop atas array gaji,\n// menambahkannya\n// dan mengembalikan hasilnya\nfunction sumSalaries(salaries) {\n  return Object.values(salaries).reduce((a, b) => a + b, 0) // 650\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/01-sum-salaries/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tambahkan propertinya\n\nAda objek `salaries` berisi beberapa gaji orang-orang.\n\nTulis fungsi `sumSalaries(salaries)` yang mengembalikan jumlah total semua gaji menggunakan `Object.values` dan loop `for..of`.\n\nJika `salaries` kosong, lalu hasilnya akan `0`.\n\nContohnya:\n\n```js\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n\nalert( sumSalaries(salaries) ); // 650\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/solution.js",
    "content": "function count(obj) { \n  return Object.keys(obj).length;\n}\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/_js.view/test.js",
    "content": "describe(\"count\", function() {\n  it(\"counts the number of properties\", function() {\n    assert.equal( count({a: 1, b: 2}), 2 );\n  });\n\n  it(\"returns 0 for an empty object\", function() {\n    assert.equal( count({}), 0 );\n  });\n\n  it(\"ignores symbolic properties\", function() {\n    assert.equal( count({ [Symbol('id')]: 1 }), 0 );\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/02-count-properties/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Count properties\n\nTulis fungsi `count(obj)` yang mengembalikan jumlah properti di dalam objek:\n\n```js\nlet user = {\n  name: 'John',\n  age: 30\n};\n\nalert( count(user) ); // 2\n```\n\nCoba buat kodenya sependek mungkin.\n\nP.S. Jangan pedulikan properti simbolik, hanya hitung yang \"reguler\".\n\n"
  },
  {
    "path": "1-js/05-data-types/09-keys-values-entries/article.md",
    "content": "\n# Objek.kunci, nilai, entri\n\nMari kita berpaling dari struktur data individual dan membahas iterasi mereka.\n\nDi bab sebelumnya kita telah melihat method `map.keys()`, `map.values()`, `map.entries()`.\n\nMethod ini generik, ada persetujuan umum untuk menggunakan mereka untuk struktur data. Jika kita pernah menciptakan struktur data sendiri, kita harus mengimplementasikannya juga.\n\nMereka tersedia untuk:\n\n- `Map`\n- `Set`\n- `Array`\n\nObjek biasa juga menghadapi method yang mirip, tapi sintaksisnya sedikit berbeda.\n\n## Objek.kunci, nilai-nilai, entri-entri\n\nUntuk objek biasa, method berikut tersedia:\n\n- [Object.keys(obj)](mdn:js/Object/keys) -- mengembalikan array kunci.\n- [Object.values(obj)](mdn:js/Object/values) -- mengembalikan array nilai.\n- [Object.entries(obj)](mdn:js/Object/entries) -- mengembalikan array pasangan `[key, value]`.\n\nPerhatikanlah perbedaannya(dibanding map contohnya):\n\n|             | Map              | Object       |\n|-------------|------------------|--------------|\n| Call syntax | `map.keys()`  | `Object.keys(obj)`, but not `obj.keys()` |\n| Returns     | iterable    | \"real\" Array                     |\n\nPerbedaan pertama adalah kita harus memanggil `Object.keys(obj)`, bukan `obj.keys()`.\n\nMengapa? Alasan pertama adalah fleksibilitas. Ingat, objek adalah dasar dari struktur kompleks di Javascript. Jadi kita mungkin mempunyai objek seperti `data` yang mengimplemen method `data.values()` sendirinya. Dan kita masih bisa memanggil `Object.values(data)` atasnya.\n\nAlasan kedua adalah method `Object.*` mengembalikan objek array \"betulan\", bukan hanya iterable. Itu terutama untuk alasan-alasan historis.\n\nContohnya:\n\n```js\nlet user = {\n  name: \"John\",\n  age: 30\n};\n```\n\n- `Object.keys(user) = [\"name\", \"age\"]`\n- `Object.values(user) = [\"John\", 30]`\n- `Object.entries(user) = [ [\"name\",\"John\"], [\"age\",30] ]`\n\nIni adalah contoh pengunaan `Object.values` untuk meng-loop atas nilai-nilai properti:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n// loop atas nilai\nfor (let value of Object.values(user)) {\n  alert(value); // John, then 30\n}\n```\n\n```warn header=\"Object.keys/values/entries abaikan properti simbolis\"\nSeperti `for..in` loop, method ini mengabaikan properti yang menggunakan `Symbol(...)` as keys.\n\nBiasanya itu mudah. Tapi jika kita mau kunci simbolis juga, ada method lain [Object.getOwnPropertySymbols](mdn:js/Object/getOwnPropertySymbols) yang mengembalikan array berisi kunci simbolis saja. Ada juga method [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) yang mengembalikan semua kunci.\n```\n\n\n## Mengubah objek\n\nObjek kekurangan banyak method yang ada untuk arrays, contoh `map`, `filter` dan yang lainnya.\n\nJika kita ingin mengapplikasikan method-method tersebut, kita bisa menggunakan `Object.entries` diikuti oleh `Object.fromEntries`:\n\n1. Gunakan `Object.entries(obj)` untuk mendapatkan array pasangan kunci/nilai dari `obj`.\n2. Gunakan method array di array tersebut, contoh `map`. \n3. Gunakan `Object.fromEntries(array)` di array hasil untuk mengubahnya kembali menjadi objek.\n\nSebagai contoh, kita mempunyai objek dengan harga-harga, dan mau melipat duakan harga-harganya:\n\n```js run\nlet prices = {\n  banana: 1,\n  orange: 2,\n  meat: 4,\n};\n\n*!*\nlet doublePrices = Object.fromEntries(\n  // ubah menjadi array, map, lalu fromEntries mengembalikan objeknya\n  Object.entries(prices).map(([key, value]) => [key, value * 2])\n);\n*/!*\n\nalert(doublePrices.meat); // 8\n```   \n\nMungkin ini terlihat susah pertama kalinya, tetapi ini akan menjadi mudah untuk di mengerti setelah kamu menggunakannya beberapa kali. Kita bisa membuat perantaian hebat dengan cara ini.\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/1-destruct-user/solution.md",
    "content": "\n```js run\nlet user = {\n  name: \"John\",\n  years: 30\n};\n\nlet {name, years: age, isAdmin = false} = user;\n\nalert( name ); // John\nalert( age ); // 30\nalert( isAdmin ); // false\n```"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/1-destruct-user/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Destrukturisasi penugasan\n\nKita mempunyai sebuah objek:\n\n```js\nlet user = {\n  name: \"John\",\n  years: 30\n};\n```\n\nTulis destrukturisasi penugasan yang terbaca:\n\n- `name` properti menjadi variabel `name`.\n- `years` properti menjadi variabel `age`.\n- `isAdmin` properti menjadi variabel `isAdmin` (false, jika tidak ada properti seperti itu)\n\nBerikut adalah contoh nilai setelah penugasan Anda:\n\n```js\nlet user = { name: \"John\", years: 30 };\n\n// kode Anda ke sisi kiri:\n// ... = user\n\nalert( name ); // John\nalert( age ); // 30\nalert( isAdmin ); // false\n```\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/solution.js",
    "content": "function topSalary(salaries) {\n\n  let maxSalary = 0;\n  let maxName = null;\n\n  for(const [name, salary] of Object.entries(salaries)) {\n    if (maxSalary < salary) {\n      maxSalary = salary;\n      maxName = name;\n    }\n  }\n\n  return maxName;\n}"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/_js.view/test.js",
    "content": "describe(\"topSalary\", function() {\n  it(\"returns top-paid person\", function() {\n    let salaries = {\n      \"John\": 100,\n      \"Pete\": 300,\n      \"Mary\": 250\n    };\n\n    assert.equal( topSalary(salaries), \"Pete\" );\n  });\n\n  it(\"returns null for the empty object\", function() {\n    assert.isNull( topSalary({}) );\n  });\n});"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/6-max-salary/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Gaji maksimal\n\nAda objek `salaries`:\n\n```js\nlet salaries = {\n  \"John\": 100,\n  \"Pete\": 300,\n  \"Mary\": 250\n};\n```\n\nBuatlah fungsi `topSalary(salaries)` yang mengembalikan nama orang dengan bayaran tertinggi.\n\n- Jika `gaji` kosong, itu harus mengembalikan` null`.\n- Jika ada beberapa orang bergaji tinggi, kembalikan salah satu dari mereka.\n\nN.B. Gunakan `Object.entries` dan destrukturisasi untuk meng-iterasi lewat pasangan kunci/nilai.\n"
  },
  {
    "path": "1-js/05-data-types/10-destructuring-assignment/article.md",
    "content": "# Destrukturisasi Penugasan\n\nDua stuktur data yang paling banyak digunakan di Javascript adalah `Object` dan `Array`\n\nObjek memungkinkan kita untuk membuat entitas tunggal yang menyimpan data item berdasarkan kunci, dan array memungkinkan kita untuk mengumpulkan data item menjadi koleksi yang terurut.\n\nTetapi ketika kita meneruskannya ke suatu fungsi, itu mungkin tidak perlu objek / array secara keseluruhan, melainkan potongan individual.\n\n*Destructuring assignment* adalah sebuah sintaks spesial yang memungkinkan kita untuk \"membongkar\" array atau objek menjadi variabel yang banyak, kadang-kadang itu memang lebih nyaman. Destrukturisasi juga berfungsi baik dengan fungsi-fungsi kompleks yang mempunyai banyak parameter, nilai default, dan sebagainya.\n\n## Destrukturisasi Array\n\nContoh bagaimana array di-destrukturisasi menjadi variabel:\n\n```js\n// kita mempunyai array dengan nama, dan nama keluarga\nlet arr = [\"John\", \"Smith\"]\n\n*!*\n// destructuring assignment\n// atur firstName = arr[0]\n// dan surname = arr[1]\nlet [firstName, surname] = arr;\n*/!*\n\nalert(firstName); // John\nalert(surname);  // Smith\n```\n\nSekarang kita bisa bekerja dengan variabel bukan anggota array.\n\nIni terlihat hebat ketika dikombinasikan dengan `split` atau metode pengembalian array lainnya:\n\n```js run\nlet [firstName, surname] = \"John Smith\".split(' ');\nalert(firstName); // John\nalert(surname);  // Smith\n```\n\n````smart header=\"\\\"Destructuring\\\" bukan berarti \\\"destructive\\\".\"\nIni disebut \"destructuring assignment,\" karena \"destructurizes\" dengan menyalin item kedalam variabel. Tetapi array itu sendiri tidak dimodifikasi.\n\nIni hanya cara singkat untuk menulis:\n```js\n// let [firstName, surname] = arr;\nlet firstName = arr[0];\nlet surname = arr[1];\n```\n````\n\n````smart header=\"Hindari elemen menggunakan koma\"\nElemen yang tidak diinginkan di array juga bisa di buang dengan sebuah koma tambahan:\n\n```js run\n*!*\n// elemen kedua tidak dibutuhkan\nlet [firstName, , title] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n*/!*\n\nalert( title ); // Consul\n```\n\nPada kode diatas, elemen kedua dari array dilewati, yang ketiga ditetapkan untuk `title`, dan sisa item array juga dilewati (karena tidak ada variabel untuknya).\n````\n\n````smart header=\"Bekerja dengan iterabel apapun di sisi kanan\"\n\n... Sebenarnya, kita bisa mengggunakan itu untuk iterasi apapun, bukan hanya array:\n\n```js\nlet [a, b, c] = \"abc\"; // [\"a\", \"b\", \"c\"]\nlet [one, two, three] = new Set([1, 2, 3]);\n```\nThat works, because internally a destructuring assignment works by iterating over the right value. It's kind of syntax sugar for calling `for..of` over the value to the right of `=` and assigning the values.\n````\n\n\n````smart header=\"Menetapkan ke apa saja pada sisi kiri\"\n\nKita bisa menggunakan \"penetapan\" apa saja pada sisi kiri.\n\nMisalnya, sebuah properti objek:\n```js run\nlet user = {};\n[user.name, user.surname] = \"John Smith\".split(' ');\n\nalert(user.name); // John\nalert(user.surname); // Smith\n```\n\n````\n\n````smart header=\"Pengulangan dengan .entries()\"\n\nDi bagian sebelumnya kita melihat metode [Object.entries(obj)](mdn:js/Object/entries).\n\nKita bisa menggunakan itu untuk destrukturisasi untuk melompati kunci-dan-nilai sebuah objek:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30\n};\n\n// loop over keys-and-values\n*!*\nfor (let [key, value] of Object.entries(user)) {\n*/!*\n  alert(`${key}:${value}`); // name:John, then age:30\n}\n```\n\nKodingan yang sama untuk sebuah `Map` lebih sederhana, dan juga bisa diiterasi:\n\n```js run\nlet user = new Map();\nuser.set(\"name\", \"John\");\nuser.set(\"age\", \"30\");\n\n*!*\n// Map iterates as [key, value] pairs, very convenient for destructuring\nfor (let [key, value] of user) {\n*/!*\n  alert(`${key}:${value}`); // name:John, then age:30\n}\n```\n````\n\n```smart header=\"Trik menukar variabel\"\nTrik yang paling diketahui untuk menukar nilai dari dua variabel:\n\n```js run\nlet guest = \"Jane\";\nlet admin = \"Pete\";\n\n// Tukar nilai: buat guest=Pete, admin=Jane\n[guest, admin] = [admin, guest];\n*/!*\n\nalert(`${guest} ${admin}`); // Pete Jane (penukaran berhasil!)\n```\n\nDisini kita membuat array sementara untuk dua variabel dan langsung memisahkannya dengan urutan penukaran.\n\nKita bisa menukar lebih daripada dua variabel dengan cara ini.\n\n\n### Sisanya '...'\n\nJika kita ingin tidak hanya mendapatkan nilai pertama, tetapi juga untuk mengumpulkan semua yang mengikuti -- kita dapat menambahkan satu parameter lagi dan mendapat \"the rest\" menggunakan tiga titik `\"...\"`:\n\n```js run\nlet [name1, name2] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n\nalert(name1); // Julius\nalert(name2); // Caesar\n// Further items aren't assigned anywhere\n```\n\nIf we'd like also to gather all that follows -- we can add one more parameter that gets \"the rest\" using three dots `\"...\"`:\n\n```js run\nlet [name1, name2, *!*...rest*/!*] = [\"Julius\", \"Caesar\", *!*\"Consul\", \"of the Roman Republic\"*/!*];\n\n*!*\n// Catatan bahwa tipe dari `rest` adalah Array.\nalert(rest[0]); // Consul\nalert(rest[1]); // of the Roman Republic\nalert(rest.length); // 2\n*/!*\n```\n\nNilai dari `rest` adalah array dari elemen array yang tersisa. Kita bisa menggunakan variabel lain apapun pada `rest`, hanya pastikan memiliki tiga titik sebelum itu dan pergi terakhir di penetapan destrukturisasi.\n\n```js run\nlet [name1, name2, *!*...titles*/!*] = [\"Julius\", \"Caesar\", \"Consul\", \"of the Roman Republic\"];\n// now titles = [\"Consul\", \"of the Roman Republic\"]\n```\n\n### Nilai default\n\nJika ada lebih sedikit nilai dalam array daripada variabel dalam penugasan, tidak akan ada kesalahan. Nilai absen dianggap undefined:\n\n```js run\n*!*\nlet [firstName, surname] = [];\n*/!*\n\nalert(firstName); // undefined\nalert(surname); // undefined\n```\n\nJika kita ingin sebuah nilai \"default\" untuk mengganti yang hilang, kita bisa menyediakan menggunakan `=`:\n\n```js run\n*!*\n// nilai default\nlet [name = \"Guest\", surname = \"Anonymous\"] = [\"Julius\"];\n*/!*\n\nalert(name);    // Julius (dari array)\nalert(surname); // Anonymous (digunakan default)\n```\n\nNilai default bisa berupa ekspresi yang lebih kompleks atau bahkan panggilan fungsi\nDefault values can be more complex expressions or even function calls. Mereka dievaluasi hanya jika nilainya tidak diberikan.\n\nSebagai contoh, di sini kita menggunakan fungsi `prompt` untuk dua default. Tapi itu hanya akan berjalan untuk yang hilang:\n\n```js run\n// prompt hanya berjalan untuk nama keluarga (surname)\nlet [name = prompt('name?'), surname = prompt('surname?')] = [\"Julius\"];\n\nalert(name);    // Julius (dari array)\nalert(surname); // apapun yang prompt dapatkan\n```\n\nPlease note: the `prompt` will run only for the missing value (`surname`).\n\n## Destrukturisasi objek\n\nPenugasan destrukturisasi juga bekerja dengan objek.\n\nSintaks dasarnya adalah:\n\n```js\nlet {var1, var2} = {var1:…, var2:…}\n```\n\nKita memiliki objek yang ada di sisi kanan, yang ingin kita pisah menjadi beberapa variabel. Sisi kiri berisi \"pola\" untuk properti yang sesuai. Dalam kasus sederhana, itu adalah daftar nama variabel di `{...}`.\n\nContohnya:\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n*!*\nlet {title, width, height} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\n```\n\nProperti `options.title`, `options.width` dan `options.height` ditugaskan ke variabel yang sesuai. Urutannya tidak masalah. Ini juga berfungsi:\n\n```js\n// mengganti urutan di let {...}\nlet {height, width, title} = { title: \"Menu\", height: 200, width: 100 }\n```\n\nPola di sisi kiri mungkin lebih kompleks dan menentukan pemetaan antara properti dan variabel.\n\nJika kita ingin menetapkan properti ke variabel dengan nama lain, misalnya, `options.width` untuk masuk ke variabel bernama` w`, maka kita dapat mengaturnya menggunakan tanda titik dua:\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n*!*\n// { sourceProperty: targetVariable }\nlet {width: w, height: h, title} = options;\n*/!*\n\n// width -> w\n// height -> h\n// title -> title\n\nalert(title);  // Menu\nalert(w);      // 100\nalert(h);      // 200\n```\n\nKolon menunjukkan \"apa : pergi kemana\". Dalam contoh di atas properti `width` pergi ke` w`, properti `height` pergi ke` h`, dan `title` ditugaskan ke nama yang sama.\n\nUntuk properti yang berpotensi hilang, kita dapat menetapkan nilai default menggunakan `\" = \"`, seperti ini:\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width = 100, height = 200, title} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\n```\n\nSama seperti dengan array atau parameter fungsi, nilai default dapat berupa ekspresi atau bahkan panggilan fungsi. Mereka akan dievaluasi jika nilainya tidak diberikan.\n\nDalam kode di bawah ini `prompt` meminta` width`, tetapi tidak untuk `title`:\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width = prompt(\"width?\"), title = prompt(\"title?\")} = options;\n*/!*\n\nalert(title);  // Menu\nalert(width);  // (apapun hasil dari prompt)\n```\n\nKita juga dapat menggabungkan titik dua dan persamaan:\n\n```js run\nlet options = {\n  title: \"Menu\"\n};\n\n*!*\nlet {width: w = 100, height: h = 200, title} = options;\n*/!*\n\nalert(title);  // Menu\nalert(w);      // 100\nalert(h);      // 200\n```\n\nJika kita memiliki objek yang kompleks dengan banyak properti, kita hanya dapat mengekstrak apa yang kita butuhkan:\n\n```js run\nlet options = {\n  title: \"Menu\",\n  width: 100,\n  height: 200\n};\n\n// hanya ekstrak judul sebagai variabel\nlet { title } = options;\n\nalert(title); // Menu\n```\n\n### Pola sisanya \"...\"\n\nBagaimana jika objek memiliki lebih banyak properti daripada variabel yang kita miliki? Bisakah kita mengambil beberapa dan kemudian menetapkan \"sisanya\" di suatu tempat?\n\nKita bisa menggunakan pola 'rest', seperti yang kita lakukan dengan array. Itu tidak didukung oleh beberapa browser tua (IE, gunakan Babel untuk mem-polyfill itu) tapi berjalan di yang modern.\n\nTerlihat seperti ini:\n\n```js run\nlet options = {\n  title: \"Menu\",\n  height: 200,\n  width: 100\n};\n\n*!*\n// title = properti bernama judul\n// rest = objek dengan sisa properti\nlet {title, ...rest} = options;\n*/!*\n\n// sekarang title=\"Menu\", rest={height: 200, width: 100}\nalert(rest.height);  // 200\nalert(rest.width);   // 100\n```\n\n````smart header=\"Gotcha jika tidak ada `let`\"\nDalam contoh-contoh di atas, variabel dinyatakan tepat dalam penugasan: `let {...} = {...}`. Tentu saja, kita bisa menggunakan variabel yang ada juga, tanpa `let`. Tapi ada tangkapan.\n\nIni tidak akan berfungsi:\n```js run\nlet title, width, height;\n\n// kesalahan di baris ini\n{title, width, height} = {title: \"Menu\", width: 200, height: 100};\n```\n\nMasalahnya adalah bahwa JavaScript memperlakukan `{...}` dalam aliran kode utama (tidak di dalam ekspresi lain) sebagai blok kode. Blok kode seperti itu dapat digunakan untuk pernyataan grup, seperti ini:\n\n```js run\n{\n  // sebuah kode blok\n  let message = \"Hello\";\n  // ...\n  alert( message );\n}\n```\n\nJadi di sini JavaScript mengasumsikan bahwa kita memiliki blok kode, itu sebabnya ada kesalahan. Kita ingin mendekstukturisasi.\n\nUntuk memperlihatkan JavaScript bahwa itu bukan blok kode, kita dapat membungkus ekspresi dalam tanda kurung `(...)`:\n\n```js run\nlet title, width, height;\n\n// oke sekarang\n*!*(*/!*{title, width, height} = {title: \"Menu\", width: 200, height: 100}*!*)*/!*;\n\nalert( title ); // Menu\n```\n````\n\n## Destrukturisasi bersarang\n\nJika suatu objek atau array berisi objek dan array bersarang lainnya, kita dapat menggunakan pola sisi kiri yang lebih kompleks untuk mengekstraksi bagian yang lebih dalam.\n\nDalam kode di bawah ini `options` memiliki objek lain di properti` size` dan sebuah array di properti `items`. Pola di sisi kiri penugasan memiliki struktur yang sama untuk mengekstrak nilai dari mereka:\n\n```js run\nlet options = {\n  size: {\n    width: 100,\n    height: 200\n  },\n  items: [\"Cake\", \"Donut\"],\n  extra: true   \n};\n\n// tugas dekstukturisasi dibagi dalam beberapa baris untuk kejelasan\nlet {\n  size: { // letakkan ukuran di sini\n    width,\n    height\n  },\n  items: [item1, item2], // tetapkan item di sini\n  title = \"Menu\" // tidak ada dalam objek (nilai default digunakan)\n} = options;\n\nalert(title);  // Menu\nalert(width);  // 100\nalert(height); // 200\nalert(item1);  // Cake\nalert(item2);  // Donut\n```\n\n\nSemua properti objek `options` kecuali` extra` yang tidak ada di bagian kiri, ditetapkan ke variabel yang sesuai:\n\n![](destructuring-complex.svg)\n\nAkhirnya, kita memiliki `width`,` height`, `item1`,` item2` dan `title` dari nilai default.\n\nPerhatikan bahwa tidak ada variabel untuk `size` dan` item`, karena kita mengambil kontennya.\n\n## Parameter fungsi cerdas\n\nAda kalanya suatu fungsi memiliki banyak parameter, yang sebagian besar bersifat opsional. Itu terutama berlaku untuk antarmuka pengguna. Bayangkan sebuah fungsi yang menciptakan menu. Mungkin memiliki lebar, tinggi, judul, daftar item dan sebagainya.\n\nBerikut cara yang buruk untuk menulis fungsi tersebut:\n\n```js\nfunction showMenu(title = \"Untitled\", width = 200, height = 100, items = []) {\n  // ...\n}\n```\n\nDalam kehidupan nyata, masalahnya adalah bagaimana cara mengingat urutan argumen. Biasanya IDE mencoba membantu kita, terutama jika kodenya didokumentasikan dengan baik, tetapi masih ... Masalah lain adalah bagaimana memanggil fungsi ketika sebagian besar parameter ok secara default.\n\nSoperti ini?\n\n```js\n// tidak ditentukan dimana nilai default baik-baik saja\nshowMenu(\"My Menu\", undefined, undefined, [\"Item1\", \"Item2\"])\n```\n\nItu jelek. Dan menjadi tidak dapat dibaca ketika kita berurusan dengan lebih banyak parameter.\n\nDestrukturisasi datang untuk menyelamatkan!\n\nKita dapat melewatkan parameter sebagai objek, dan fungsinya segera merusaknya menjadi variabel:\n\n```js run\n// kita meneruskan objek ke fungsi\nlet options = {\n  title: \"My menu\",\n  items: [\"Item1\", \"Item2\"]\n};\n\n// ...dan segera memperluasnya ke variabel\nfunction showMenu(*!*{title = \"Untitled\", width = 200, height = 100, items = []}*/!*) {\n  // title, items – diambil dari options,\n  // width, height – standar yang digunakan\n  alert( `${title} ${width} ${height}` ); // My Menu 200 100\n  alert( items ); // Item1, Item2\n}\n\nshowMenu(options);\n```\n\nKita juga dapat menggunakan perusakan yang lebih kompleks dengan objek bersarang dan pemetaan titik dua:\n\n```js run\nlet options = {\n  title: \"My menu\",\n  items: [\"Item1\", \"Item2\"]\n};\n\n*!*\nfunction showMenu({\n  title = \"Untitled\",\n  width: w = 100,  // width menjadi w\n  height: h = 200, // height menjadi h\n  items: [item1, item2] // element pertama items menjadi item1, kedua menjadi item2\n}) {\n*/!*\n  alert( `${title} ${w} ${h}` ); // My Menu 100 200\n  alert( item1 ); // Item1\n  alert( item2 ); // Item2\n}\n\nshowMenu(options);\n```\n\nSintaks lengkapnya sama dengan untuk tugas penataan:\n```js\nfunction({\n  incomingProperty: varName = defaultValue\n  ...\n})\n```\n\nKemudian, untuk objek parameter, akan ada variabel `varName` untuk properti` incomingProperty`, dengan `defaultValue` secara default.\n\nHarap perhatikan bahwa destrukturisasi seperti itu mengasumsikan bahwa `showMenu ()` memang memiliki argumen. Jika kita menginginkan semua nilai secara default, maka kita harus menentukan objek kosong:\n\n```js\nshowMenu({}); // ok, semua nilai adalah default\n\nshowMenu(); // ini akan memberikan kesalahan\n```\n\nKita dapat memperbaikinya dengan menjadikan `{}` nilai default untuk seluruh objek parameter:\n\n```js run\nfunction showMenu({ title = \"Menu\", width = 100, height = 200 }*!* = {}*/!*) {\n  alert( `${title} ${width} ${height}` );\n}\n\nshowMenu(); // Menu 100 200\n```\n\nDalam kode di atas, objek argumen keseluruhan adalah `{}` secara default, jadi selalu ada sesuatu untuk distrukturisasi.\n\n## Ringkasan\n\n- Penugasan destrukturisasi memungkinkan untuk memetakan objek atau array secara instan ke banyak variabel.\n- Sintaks lengkap objek:\n    ```js\n    let {prop : varName = default, ...rest} = object\n    ```\n\n    Ini berarti properti `prop` harus masuk ke variabel` varName` dan, jika tidak ada properti seperti itu, maka nilai `default` harus digunakan.\n\n    Properti objek yang tidak memiliki pemetaan disalin ke objek `rest`.\n\n- Sintaks lengkap array:\n\n    ```js\n    let [item1 = default, item2, ...rest] = array\n    ```\n\n    Item pertama masuk ke `item1`; yang kedua masuk ke `item2`, sisanya membuat array `rest`.\n\n- Dimungkinkan untuk mengekstraksi data dari array / objek bersarang, untuk itu sisi kiri harus memiliki struktur yang sama dengan yang benar.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/1-new-date/solution.md",
    "content": "Konstruktor `new Date` menggunakan zona waktu lokal. Sehingga hal penting yang harus diingat adalah bulan dimulai dari angka 0.\n\nJadi Februari mempunyai angka 1.\n\nHere's an example with numbers as date components:\n\n```js run\n//new Date(year, month, date, hour, minute, second, millisecond)\nlet d1 = new Date(2012, 1, 20, 3, 12);\nalert( d1 );\n```\nWe could also create a date from a string, like this:\n\n```js run\n//new Date(datastring)\nlet d2 = new Date(\"February 20, 2012 03:12:00\");\nalert( d2 );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/1-new-date/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Buat sebuah tanggal\n\nBuat sebuah objek `Date` untuk tanggal: Feb 20, 2012, 3:12am. Zona waktu lokal.\n\nTampilkan menggunakan `alert`.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/_js.view/solution.js",
    "content": "function getWeekDay(date) {\n  let days = ['MIN', 'SEN', 'SEL', 'RAB', 'KAM', 'JUM', 'SAB'];\n\n  return days[date.getDay()];\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/_js.view/test.js",
    "content": "describe(\"getWeekDay\", function() {\n  it(\"3 January 2014 - friday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 3)), 'JUM');\n  });\n\n  it(\"4 January 2014 - saturday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 4)), 'SAB');\n  });\n\n  it(\"5 January 2014 - sunday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 5)), 'MIN');\n  });\n\n  it(\"6 January 2014 - monday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 6)), 'SEN');\n  });\n\n  it(\"7 January 2014 - tuesday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 7)), 'SEL');\n  });\n\n  it(\"8 January 2014 - wednesday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 8)), 'RAB');\n  });\n\n  it(\"9 January 2014 - thursday\", function() {\n    assert.equal(getWeekDay(new Date(2014, 0, 9)), 'KAM');\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/solution.md",
    "content": "Metode `date.getDay()` mengembalikan angka dari hari dalam satu minggu, dimulai dari Minggu.\n\nBuat array hari dalam seminggu, sehingga kita bisa mendapatkan nama yang sesuai dengan angkanya:\n\n```js run demo\nfunction getWeekDay(date) {\n  let days = ['MIN', 'SEN', 'SEL', 'RAB', 'KAM', 'JUM', 'SAB'];\n\n  return days[date.getDay()];\n}\n\nlet date = new Date(2014, 0, 3); // 3 Jan 2014\nalert( getWeekDay(date) ); // JUM\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/2-get-week-day/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Tampilkan hari dalam satu minggu \n\nTulis sebuah fungsi `getWeekDay(tanggal)` untuk menunjukkan hari dalam format: 'SEN', 'SEL', 'RAB', 'KAM', 'JUM', 'SAB', 'MIN'.\n\nSebagai contoh:\n\n```js no-beautify\nlet tanggal = new Date(2012, 0, 3);  // 3 Jan 2012\nalert( getWeekDay(tanggal) );        // harus mengeluarkan \"SEL\"\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/_js.view/solution.js",
    "content": "function getLocalDay(date) {\n\n  let days = date.getDay();\n\n  if (days == 0) { // hari ke-0 (Minggu) adalah hari ke-7 di Eropa\n    days = 7;\n  }\n\n  return days;\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/_js.view/test.js",
    "content": "describe(\"getLocalDay mengembalikan hari sesuai dengan standar di \\\"eropa\\\" \", function() {\n  it(\"3 January 2014 - Jumat\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 3)), 5);\n  });\n\n  it(\"4 January 2014 - Sabtu\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 4)), 6);\n  });\n\n  it(\"5 January 2014 - Minggu\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 5)), 7);\n  });\n\n  it(\"6 January 2014 - Senin\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 6)), 1);\n  });\n\n  it(\"7 January 2014 - Selasa\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 7)), 2);\n  });\n\n  it(\"8 January 2014 - Rabu\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 8)), 3);\n  });\n\n  it(\"9 January 2014 - Kamis\", function() {\n    assert.equal(getLocalDay(new Date(2014, 0, 9)), 4);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/solution.md",
    "content": ""
  },
  {
    "path": "1-js/05-data-types/11-date/3-weekday/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Hari di Eropa\n\nNegara-negara di Eropa mempunyai hari yang dimulai dari Senin (angka 1), kemudian Selasa (angka 2) sampai dengan Minggu (angka 7). Buatlah sebuah fungsi `getLocalDay(date)` yang mengembalikan hari sesuai dengan standar tanggal di Eropa.\n\n```js no-beautify\nlet date = new Date(2012, 0, 3);  // 3 Jan 2012\nalert( getLocalDay(date) );       // Selasa, seharusnya menunjukkan angka 2\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/_js.view/solution.js",
    "content": "function getDateAgo(date, days) {\n  let dateCopy = new Date(date);\n\n  dateCopy.setDate(date.getDate() - days);\n  return dateCopy.getDate();\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/_js.view/test.js",
    "content": "describe(\"getDateAgo\", function() {\n\n  it(\"1 hari sebelum 02.01.2015 -> tanggal 1\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 1), 1);\n  });\n\n\n  it(\"2 hari sebelum 02.01.2015 -> tanggal 31\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 2), 31);\n  });\n\n  it(\"100 hari sebelum 02.01.2015 -> tanggal 24\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 100), 24);\n  });\n\n  it(\"365 hari sebelum 02.01.2015 -> tanggal 2\", function() {\n    assert.equal(getDateAgo(new Date(2015, 0, 2), 365), 2);\n  });\n\n  it(\"tidak mengubah tanggal yang diberikan\", function() {\n    let tanggal = new Date(2015, 0, 2);\n    let tanggalCopy = new Date(tanggal);\n    getDateAgo(tanggalCopy, 100);\n    assert.equal(tanggal.getTime(), tanggalCopy.getTime());\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/solution.md",
    "content": "Idenya mudah: Kurangi `tanggal` dengan jumlah hari yang diberikan.\n\n```js\nfunction getDateAgo(tanggal, hari) {\n  tanggal.setDate(tanggal.getDate() - hari);\n  return tanggal.getDate();\n}\n```\n\n...Namun fungsi tersebut tidak boleh mengubah `tanggal` yang diberikan. Ini adalah yang terpenting, karena kode di luar yang memberikan kita tanggal tidak mengira tanggal tersebut akan berubah. \n\nUntuk mengimplementasikannya kita akan menduplikasi tanggal tersebut, seperti ini:\n\n```js run demo\nfunction getDateAgo(tanggal, hari) {\n  let tanggalCopy = new Date(tanggal);\n\n  tanggalCopy.setDate(tanggal.getDate() - hari);\n  return tanggalCopy.getDate();\n}\n\nlet tanggal = new Date(2015, 0, 2);\n\nalert( getDateAgo(tanggal, 1) ); // 1, (1 Jan 2015)\nalert( getDateAgo(tanggal, 2) ); // 31, (31 Des 2014)\nalert( getDateAgo(tanggal, 365) ); // 2, (2 Jan 2014)\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/4-get-date-ago/task.md",
    "content": "Tingkat kepentingan: 4\n\n---\n\n# Tanggal berapakah beberapa hari yang lalu? \n\nBuatlah sebuah fungsi `getDateAgo(tanggal, hari)` yang mengembalikan beberapa `hari` yang telah berlalu dari sebuah `tanggal`. \n\nSebagai contoh, apabila hari ini tanggal 20, maka `getDateAgo(new Date(), 1)` harus mengembalikan tanggal 19 dan `getDateAgo(new Date(), 2)` harus mengembalikan tanggal 18.\n\nHarus bekerja dengan baik dan dapat diandalkan untuk `hari=365` atau lebih:\n\n```js\nlet tanggal = new Date(2015, 0, 2);\n\nalert( getDateAgo(tanggal, 1) ); // 1, (1 Jan 2015)\nalert( getDateAgo(tanggal, 2) ); // 31, (31 Des 2014)\nalert( getDateAgo(tanggal, 365) ); // 2, (2 Jan 2014)\n```\n\nP.S. Fungsi tidak boleh mengubah `tanggal` yang diberikan. \n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/_js.view/solution.js",
    "content": "function getLastDayOfMonth(tahun, bulan) {\n  let tanggal = new Date(tahun, bulan + 1, 0);\n  return tanggal.getDate();\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/_js.view/test.js",
    "content": "describe(\"getLastDayOfMonth\", function() {\n  it(\"tanggal terakhir dari 01.01.2012 - 31\", function() {\n    assert.equal(getLastDayOfMonth(2012, 0), 31);\n  });\n\n  it(\"tanggal terakhir dari 01.02.2012 - 29 (tahun kabisat)\", function() {\n    assert.equal(getLastDayOfMonth(2012, 1), 29);\n  });\n\n  it(\"tanggal terakhir dari 01.02.2013 - 28\", function() {\n    assert.equal(getLastDayOfMonth(2013, 1), 28);\n  });\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/solution.md",
    "content": "Buat sebuah tanggal menggunakan bulan berikutnya, namun berikan 0 sebagai tanggalnya:\n```js run demo\nfunction getLastDayOfMonth(tahun, bulan) {\n  let tanggal = new Date(tahun, bulan + 1, 0);\n  return tanggal.getDate();\n}\n\nalert( getLastDayOfMonth(2012, 0) ); // 31\nalert( getLastDayOfMonth(2012, 1) ); // 29\nalert( getLastDayOfMonth(2013, 1) ); // 28\n```\n\nNormalnya, tanggal dimulai dari 1, namun secara teknis kita bisa berikan angka apapun, dan tanggal akan otomatis menyesuaikan. Sehingga ketika kita berikan 0, itu berarti \"satu hari sebelum tanggal pertama untuk sebuah bulan\", dengan kata lain: \"hari terakhir pada bulan sebelumnya\".\n"
  },
  {
    "path": "1-js/05-data-types/11-date/5-last-day-of-month/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Tanggal terakhir dari sebuah bulan?\n\nTulis sebuah fungsi `getLastDayOfMonth(tahun, bulan)` yang mengembalikan tanggal terakhir dari sebuah bulan. Terkadang 30, 31, atau bahkan 28/29 untuk Februari.\n\nParameter:\n\n- `tahun` -- tahun dalam empat-digit, sebagai contoh 2012.\n- `bulan` -- bulan, dari 0 sampai 11.\n\nSebagai contoh, `getLastDayOfMonth(2012, 1) = 29` (tahun kabisat, Feb).\n"
  },
  {
    "path": "1-js/05-data-types/11-date/6-get-seconds-today/solution.md",
    "content": "Untuk mendapatkan jumlah detik, kita harus membuat sebuah tanggal menggunakan hari yang sedang berlangsung dan waktu 00:00:00, dan mengurangi waktu \"saat ini\" dengannya.\n\nPerbedaan yang didapat adalah angka dalam milidetik sejak permulaan hari, yang harus dibagi dengan 1000 agar menjadi detik:\n\n```js run\nfunction getSecondsToday() {\n  let sekarang = new Date();\n\n  // Buat sebuah objek menggunakan hari/bulan/tahun saat ini\n  let hariIni = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n\n  let beda = sekarang - hariIni; // beda dalam milidetik\n  return Math.round(beda / 1000); // ubah menjadi detik\n}\n\nalert( getSecondsToday() );\n```\n\nSolusi alternatif adalah cari jam/menit/detik dan ubah menjadi detik:\n\n```js run\nfunction getSecondsToday() {\n  let d = new Date();\n  return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();\n}\n\nalert( getSecondsToday() );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/6-get-seconds-today/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Berapa detik yang telah berlalu untuk hari ini?\n\nTulis sebuah fungsi `getSecondsToday()` yang mengembalikan angka dari detik yang telah berlalu dari sejak permulaan hari ini.\n\nSebagai contoh, sekarang pukul `10:00 am`, dan tidak ada daylight savings shift, maka:\n\n```js\ngetSecondsToday() == 36000 // (3600 * 10)\n```\n\nFungsi haruslah berjalan untuk hari apapun. sehingga, tidak boleh ada nilai \"hari ini\" yang ditulis secara hard-code.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/solution.md",
    "content": "Untuk mendapatkan jumlah milidetik hingga besok, kita bisa mendapatkannya melalui pengurangan tanggal hari ini dengan \"besok 00:00:00\"\n\nPertama, kita tentukan \"besok\", dan kemudian lakukan perhitungan:\n\n```js run\nfunction getSecondsToTomorrow() {\n  let sekarang = new Date();\n\n  // besok\n  let besok = new Date(now.getFullYear(), now.getMonth(), *!*now.getDate()+1*/!*);\n\n  let beda = besok - sekarang; // beda dalam milidetik\n  return Math.round(beda / 1000); // ubah ke detik \n}\n```\n\nSolusi alternatif:\n\n```js run\nfunction getSecondsToTomorrow() {\n  let sekarang = new Date();\n  let jam = sekarang.getHours();\n  let menit = sekarang.getMinutes();\n  let detik = sekarang.getSeconds();\n  let detikTotalHariIni = (jam * 60 + menit) * 60 + detik;\n  let detikTotalDalamSatuHari = 86400;\n\n  return detikTotalDalamSatuHari - detikTotalHariIni;\n}\n```\n\nHarap diingat bahwa banyak negara menerapkan Daylight Savings Time (DST), sehingga memungkinkan ada hari dengan 23 atau 25 jam. Kita mungkin ingin melakukan perhitungan dengan cara yang berbeda untuk mereka.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/7-get-seconds-to-tomorrow/task.md",
    "content": "Tingkat kepentingan: 5\n\n---\n\n# Berapa detik lagi sampai besok?\n\nBuat sebuah fungsi `getSecondsToTomorrow()` yang mengembalikan jumlah detik yang tersisa hingga esok hari.\n\nSebagai contoh, jika sekarang `23:00`, maka:\n\n```js\ngetSecondsToTomorrow() == 3600\n```\n\nP.S. Fungsi haruslah berjalan untuk semua hari, sehingga \"hari ini\" tidak bisa dalam bentuk hard-code.\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/_js.view/solution.js",
    "content": "\nfunction formatDate(date) {\n  let diff = new Date() - date; // perbedaan dalam mili-detik\n\n  if (diff < 1000) { // kurang dari 1 detik\n    return 'right now';\n  }\n\n  let sec = Math.floor(diff / 1000); // ubah diff menjadi detik\n\n  if (sec < 60) {\n    return sec + ' sec. ago';\n  }\n\n  let min = Math.floor(diff / 60000); // ubah diff menjadi menit\n  if (min < 60) {\n    return min + ' min. ago';\n  }\n\n  // format tanggalnya\n  // tambah awalan nol ke satu-digit hari/bulan/jam/menit\n  let d = date;\n  d = [\n    '0' + d.getDate(),\n    '0' + (d.getMonth() + 1),\n    '' + d.getFullYear(),\n    '0' + d.getHours(),\n    '0' + d.getMinutes()\n  ].map(component => component.slice(-2)); // ambil 2 dijit dari setiap komponen\n\n  // satukan komponen menjadi tanggal\n  return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');\n}\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/_js.view/test.js",
    "content": "describe(\"formatDate\", function() {\n  it(\"shows 1ms ago as \\\"right now\\\"\", function() {\n    assert.equal(formatDate(new Date(new Date - 1)), 'right now');\n  });\n\n  it('\"30 seconds ago\"', function() {\n    assert.equal(formatDate(new Date(new Date - 30 * 1000)), \"30 sec. ago\");\n  });\n\n  it('\"5 minutes ago\"', function() {\n    assert.equal(formatDate(new Date(new Date - 5 * 60 * 1000)), \"5 min. ago\");\n  });\n\n  it(\"older dates as DD.MM.YY HH:mm\", function() {\n    assert.equal(formatDate(new Date(2014, 2, 1, 11, 22, 33)), \"01.03.14 11:22\");\n  });\n\n});\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/solution.md",
    "content": "Untuk mendapatkan waktu dari `date` sampai sekarang -- kita kurangi tanggalnya.\n\n```js run demo\nfunction formatDate(date) {\n  let diff = new Date() - date; // perbedaan dalam mili-detik\n\n  if (diff < 1000) { // kurang dari 1 detik\n    return 'right now';\n  }\n\n  let sec = Math.floor(diff / 1000); // ubah diff menjadi detik\n\n  if (sec < 60) {\n    return sec + ' sec. ago';\n  }\n\n  let min = Math.floor(diff / 60000); // ubah diff menjadi menit\n  if (min < 60) {\n    return min + ' min. ago';\n  }\n\n  // format tanggalnya\n  // tambahkan awalan nol ke dijit-tunggal hari/bulan/jam/menit\n  let d = date;\n  d = [\n    '0' + d.getDate(),\n    '0' + (d.getMonth() + 1),\n    '' + d.getFullYear(),\n    '0' + d.getHours(),\n    '0' + d.getMinutes()\n  ].map(component => component.slice(-2)); // ambil setidaknya 2 dijit dari setiap komponen\n\n  // satukan komponen menjadi tanggal\n  return d.slice(0, 3).join('.') + ' ' + d.slice(3).join(':');\n}\n\nalert( formatDate(new Date(new Date - 1)) ); // \"sekarang\"\n\nalert( formatDate(new Date(new Date - 30 * 1000)) ); // \"30 detik yang lalu\"\n\nalert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // \"5 menit yang lalu\"\n\n// tanggal kemarin seperti 31.12.2016 20:00\nalert( formatDate(new Date(new Date - 86400 * 1000)) );\n```\n\nAlternative solution:\n\n```js run\nfunction formatDate(date) {\n  let dayOfMonth = date.getDate();\n  let month = date.getMonth() + 1;\n  let year = date.getFullYear();\n  let hour = date.getHours();\n  let minutes = date.getMinutes();\n  let diffMs = new Date() - date;\n  let diffSec = Math.round(diffMs / 1000);\n  let diffMin = diffSec / 60;\n  let diffHour = diffMin / 60;\n\n  // formatting\n  year = year.toString().slice(-2);\n  month = month < 10 ? '0' + month : month;\n  dayOfMonth = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth;\n  hour = hour < 10 ? '0' + hour : hour;\n  minutes = minutes < 10 ? '0' + minutes : minutes;\n\n  if (diffSec < 1) {\n    return 'right now';  \n  } else if (diffMin < 1) {\n    return `${diffSec} sec. ago`\n  } else if (diffHour < 1) {\n    return `${diffMin} min. ago`\n  } else {\n    return `${dayOfMonth}.${month}.${year} ${hour}:${minutes}`\n  }\n}\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/8-format-date-relative/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Ubah menjadi tanggal yang berhubungan\n\nTulis sebuah fungsi `formatDate(date)` yang harus memformat `date` seperti berikut:\n\n- Jika sejak `date` lewat kurang dari 1 detik, lalu `\"sekarang\"`.\n- Sebaliknya, jika sejak `date` lewat kurang dari satu menit, lalu `\"n detik yang lalu\"`.\n- Sebaliknya, jika kurang dari satu jam, lalu `\"m menit yang lalu\"`.\n- Sebaliknya, tanggal dalam format penuh `\"DD.MM.YY HH:mm\"`. Itu adalah: `\"hari.bulan.tahun jam:menit\"`, semua dalam format 2 angka, contoh. `31.12.16 10:00`.\n\nContoh:\n\n```js\nalert( formatDate(new Date(new Date - 1)) ); // \"sekarang\"\n\nalert( formatDate(new Date(new Date - 30 * 1000)) ); // \"30 detik yang lalu\"\n\nalert( formatDate(new Date(new Date - 5 * 60 * 1000)) ); // \"5 menit yang lalu\"\n\n// tanggal kemarin seperti 31.12.16 20:00\nalert( formatDate(new Date(new Date - 86400 * 1000)) );\n```\n"
  },
  {
    "path": "1-js/05-data-types/11-date/article.md",
    "content": "# Tanggal dan waktu\n\nAyo kita bertemu dengan objek bawaan baru: [Date](mdn:js/Date). yang akan menyimpan tanggal, waktu dan menyediakan metode untuk manajemen tanggal/waktu.\n\nContoh, kita bisa menggunakan itu untuk menyimpan pembuatan/modifikasi waktu, untuk menghitung waktu atau hanya untuk melihat tanggal sekarang.\n\n## Pembuatan\n\nUntuk membuat objek `Date` baru panggil `new Date()` dengan salah satu dari argumen dibawah:\n\n`new Date()`\n: Tanpa argumen -- membuat sebuah objek `Date` untuk tanggal dan waktu sekarang:\n\n    ```js run\n    let now = new Date();\n    alert( now ); // tampilkan tanggal/waktu sekarang\n    ```\n\n`new Date(milliseconds)`\n: Membuat sebuah objek `Date` dengan waktu yang sama dengan mili-detik (1/1000 dari satu detik) lewat dari Januari 1 1970 UTC+0.\n\n    ```js run\n    // 0 berarti 01.01.1970 UTC+0\n    let Jan01_1970 = new Date(0);\n    alert( Jan01_1970 );\n\n    // sekarang tambahkan 24 jam, ambil 02.01.1970 UTC+0\n    let Jan02_1970 = new Date(24 * 3600 * 1000);\n    alert( Jan02_1970 );\n    ```\n\n    Sebuah angka integer merepresentasikan angka dari milidetik yang telah lewat sejak awal dari 1970 dipanggil dengan *timestamp*.\n\n    Ini adalah angka numerik ringan yang merepresentasikan sebuah tanggal. Kita akan selalu bisa membuat tanggal dari timestamp menggunakan `new Date(timestamp)` dan mengubah objek `Date` yang ada ke sebuah timestamp dengan menggunakan metode `date.getTime()` (lihat dibawah).\n\n    Tanggal sebelum 01.01.1970 mempunyai timestamp yang negatif, contoh.:\n    ```js run\n    // 31 Dec 1969\n    let Dec31_1969 = new Date(-24 * 3600 * 1000);\n    alert( Dec31_1969 );\n    ```\n\n`new Date(datestring)`\n: Jika terdapat sebuah argumen tunggal, dan itu adalah sebuah string, lalu itu akan diurai secara otomatis. Algoritmanya sama dengan yang digunakan `Date.parse`, kita akan pelajari itu nanti.\n\n    ```js run\n    let date = new Date(\"2017-01-26\");\n    alert(date);\n    // Waktunya belum di set, jadi itu diasumsikan tengah malam GMT dan\n    // disesuaikan menurut zona waktu dimana kodenya berjalan\n    // Jadi hasilnya mungkin bisa\n    // Kamis Jan 26 2017 11:00:00 GMT+1100 (Waktu timur siang hari Australia )\n    // atau\n    // Rabu Jan 25 2017 16:00:00 GMT-0800 (Waktu standar pasifik)\n    ```\n\n`new Date(year, month, date, hours, minutes, seconds, ms)`\n: Membuat waktu dengan komponen yang diberikan dari zona waktu lokal. Hanya dua argument pertama yang wajib.\n\n    - `Tahun`nya harus mempunyai 4 angka: `2013` boleh, `98` tidak boleh.\n    - Perhitungan `Bulan`nya dimulai dari `0` (Jan), sampai `11` (Des).\n    - Parameter `date` sebenarnya adalah hari dari bulan, jika tidak ada maka akan diasumsikan `1`.\n    - Jika `jam/menit/detik/milidetik` tidak ada, mereka akan diasumsikan sama dengan `0`.\n\n    Contoh:\n\n    ```js\n    new Date(2011, 0, 1, 0, 0, 0, 0); // 1 Jan 2011, 00:00:00\n    new Date(2011, 0, 1); // sama, jam dan yang lainnya secara default adalah 0\n    ```\n\n    Presisi paling minimal adalah 1ms (1/1000 detik):\n\n    ```js run\n    let date = new Date(2011, 0, 1, 2, 3, 4, 567);\n    alert( date ); // 1.01.2011, 02:03:04.567\n    ```\n\n## Mengakses komponen tanggal\n\nTerdapat beberapa metode untuk mengakses tahun, bulan dan lainnya dari objek `Date`:\n\n[getFullYear()](mdn:js/Date/getFullYear)\n: Mendapatkan tahun (4 angka)\n\n[getMonth()](mdn:js/Date/getMonth)\n: Mendapatkan bulan, **dari 0 sampai 11**.\n\n[getDate()](mdn:js/Date/getDate)\n: mendapatkan hari dari bulan, dari 1 sampai 31, nama dari metodenya sedikit terlihat aneh.\n\n[getHours()](mdn:js/Date/getHours), [getMinutes()](mdn:js/Date/getMinutes), [getSeconds()](mdn:js/Date/getSeconds), [getMilliseconds()](mdn:js/Date/getMilliseconds)\n: Mendapatkan komponen-komponen yang bersangkutan.\n\n```warn header=\"Bukan `getYear()`, Tapi `getFullYear()`\"\nBanyak mesin Javascript mengimplementasikan metode yang tidak-standar `getYear()`. Metode ini sudah usang. Itu terkadang mengembalikan tahun dengan 2-angka. Tolong jangan gunakan itu. Gunakan `getFullYear()` untuk tahun.\n```\n\nSebagai tambahan, kita bisa mendapatkan hari dari minggu:\n\n[getDay()](mdn:js/Date/getDay)\n: Dapatkan hari dari minggu, dimulai dari `0` (Minggu) to `6` (Sabtu). Hari pertama akan selalu Minggu, di beberapa negara bukanlah minggu, dan tidak bisa diubah.\n\n**Semua metode diatas mengembalikan komponen yang bersangkutan dengan zona waktu lokal.**\n\nJuga terdapat pasangan-UTC, yang mengembalikan hari bulan, tahun dan lainnya untuk zona waktu UTC+0: [getUTCFullYear()](mdn:js/Date/getUTCFullYear), [getUTCMonth()](mdn:js/Date/getUTCMonth), [getUTCDay()](mdn:js/Date/getUTCDay). Hanya dengan memasukan `\"UTC\"` tepat setelah `\"get\"`.\n\nJika zona waktu lokal kamu diubah menjadi zona yang berhubungan dengan UTC, maka kode dibawah akan menunjukan waktu yang berbeda.\n\n```js run\n// tanggal sekarang\nlet date = new Date();\n\n// jam didalam zona waktu kamu sekarang\nalert( date.getHours() );\n\n// jam di zona waktu UTC+0 (waktu london tanpa waktu musim panas)\nalert( date.getUTCHours() );\n```\n\nDisamping metode yang diberikan, disana terdapat dua yang spesial yang tidak memiliki variasi waktu UTC:\n\n[getTime()](mdn:js/Date/getTime)\n: Mengembalikan timestamp untuk tanggal -- sebuah angka dari milidetik yang telah terlewat sejak 1 Januari 1970 UTC+0\n\n[getTimezoneOffset()](mdn:js/Date/getTimezoneOffset)\n: Mengembalikan perbedaan diantara UTC dan zona waktu lokal, dalam menit:\n\n    ```js run\n    // jika kamu berada didalam zona waktu UTC-1, mengeluarkan 60\n    // jika kamu berada di zona waktu UTC+3, mengeluarkan -180\n    alert( new Date().getTimezoneOffset() );\n\n    ```\n\n## Menyetel komponen tanggal\n\nMetode berikut memperbolehkan kita untuk menyetel komponen tanggal/waktu:\n\n- [`setFullYear(year, [month], [date])`](mdn:js/Date/setFullYear)\n- [`setMonth(month, [date])`](mdn:js/Date/setMonth)\n- [`setDate(date)`](mdn:js/Date/setDate)\n- [`setHours(hour, [min], [sec], [ms])`](mdn:js/Date/setHours)\n- [`setMinutes(min, [sec], [ms])`](mdn:js/Date/setMinutes)\n- [`setSeconds(sec, [ms])`](mdn:js/Date/setSeconds)\n- [`setMilliseconds(ms)`](mdn:js/Date/setMilliseconds)\n- [`setTime(milliseconds)`](mdn:js/Date/setTime) (setel seluruh tanggal dengan milidetik sejak 01.01.1970 UTC)\n\nSemuanya kecuali salah satunya yaitu `setTime()` mempunyai varian-UTC, contoh: `setUTCHours()`.\n\nSeperti yang bisa kita lihat, beberapa metode bisa menyetel beberapa komponen sekaligus, untuk contoh `setHours`. Komponen yang tidak disebutkan tidak akan diubah.\n\nContoh:\n\n```js run\nlet today = new Date();\n\ntoday.setHours(0);\nalert(today); // masih hari ini, tapi jamnya diubah menjadi 0\n\ntoday.setHours(0, 0, 0, 0);\nalert(today); // masih hari ini, tapi tepat 00:00:00\n```\n\n## Koreksi otomatis\n\n*Koreksi otomatis* adalah fitur yang sangat berguna dari objek `Date`. Kita bisa menyetel nilai yang diluar jangkauan, dan itu akan menyesuaikan dirinya sendiri.\n\nContoh:\n\n```js run\nlet date = new Date(2013, 0, *!*32*/!*); // 32 Jan 2013 ?!?\nalert(date); // ...adalah 1st Feb 2013!\n```\n\nkomponen tanggal yang diluar jangkauan akan diganti secara otomatis.\n\nKita bisa berkata untuk menambah tanggal \"28 feb 2016\" dengan 2 hari. Itu mungkin akan \"2 maret\" atau \"1 maret\" didalam kasus tahun kabisat. Kita tidak perlu memikirkan hal itu. Tinggal tambah 2 hari. Objek `Date` akan melakukan sisanya:\n\n```js run\nlet date = new Date(2016, 1, 28);\n*!*\ndate.setDate(date.getDate() + 2);\n*/!*\n\nalert( date ); // 1 Mar 2016\n```\n\nFitur itu sering digunakan untuk mendapatkan tanggal setelah diberikan waktu yang ditentukan, coba dapatkan tanggal \"70 detik setelah saat ini\":\n\n```js run\nlet date = new Date();\ndate.setSeconds(date.getSeconds() + 70);\n\nalert( date ); // menampilkan tanggal yang benar\n```\n\nKita juga bisa menyetel nol atau bahkan nilai negatif. Contoh:\n\n```js run\nlet date = new Date(2016, 0, 2); // 2 Jan 2016\n\ndate.setDate(1); // setel hari pertama dari bulan\nalert( date );\n\ndate.setDate(0); // kurangi 1 hari, jadi diasumsikan hari terakhir di bulan sebelumnya\n\nalert( date ); // 31 Desember 2015\n```\n\n## Tanggal menjadi angka, perbedaan tanggal\n\n\nKetika sebuah objek `Date` diubah menjadi angka, itu menjadi timestamp sama seperti `date.getTime()`:\n\n```js run\nlet date = new Date();\nalert(+date); // angka dari milidetik, sama seperti date.getTime()\n```\n\nEfek yang perlu diperhatikan: tanggal bisa dikurangi, hasilnya adalah perbedaan dalam milidetik.\n\nHal itu bisa gunakan untuk mengukur waktu:\n\n```js run\nlet start = new Date(); // mulai pengukuran waktu\n\n// lakukan perhitungannya\nfor (let i = 0; i < 100000; i++) {\n  let doSomething = i * i * i;\n}\n\nlet end = new Date(); // akhiri pengukuran waktu\n\nalert( `The loop took ${end - start} ms` );\n```\n\n## Date.now()\n\nJika kita ingin mengukur waktu, kita tidak butuh objek `Date`.\n\nTerdapat metode spesial `Date.now()` yang mengembalikan timestamp saat ini.\n\nItu secara semaktik sama dengan `new Date().getTime()`, tapi itu tidak menciptakan sebuah perantara objek `Date`. Jadi itu lebih cepat dan tidak menambah beban pembuangan sampah.\n\nKebanyakan itu digunakan untuk kenyamanan atau ketika performansi menjadi hal yang diperhatikan, seperti permainan didalam Javascript atau aplikasi yang terspesialisasi lainnya.\n\nJadi ini mungkin lebih baik:\n\n```js run\n*!*\nlet start = Date.now(); // milidetik dihitung dari 1 Januari 1970\n*/!*\n\n// lakukan perhitungannya\nfor (let i = 0; i < 100000; i++) {\n  let doSomething = i * i * i;\n}\n\n*!*\nlet end = Date.now(); // selesai\n*/!*\n\nalert( `The loop took ${end - start} ms` ); // kurangi angka, bukan tanggal\n```\n\n## Menguji kemampuan / Benchmarking\n\nJika kita ingin kemampuan yang dapat diandalkan dari fungsi yang haus akan sumberdaya CPU, kita harus hati-hati.\n\nContoh, coba kita bandingkan dua fungsi yang mengkalkulasikan perbedaan diantara dua tanggal: yang mana yang lebih cepat?\n\nPengukurang performa seperti itu sering disebut dengan \"benchmarks\".\n\n```js\n// kita punya date1 dan date2, fungsi yang mana yang lebih cepat mengembalikan perbedaannya dalam milidetik?\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\n// atau\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n```\n\nkedua fungsi itu melakukan hal yang sama persis, tapi satu dari mereka menggunakan `date.getTime()` secara eksplisit untuk mendapatkan tanggal dalam milidetik, dan lainnya menggunakan perubahan tanggal-ke-angka. Hasil mereka akan selalu sama.\n\nJadi, yang mana yang lebih cepat?\n\nCara sederhananya mungkin menjalankan mereka beberapa kali dan menghitung perbedaan waktunya. Untuk kasus ini, fungsi sangatlah sederhana, jadi kita hanya harus melakukannya setidaknya 100000 kali.\n\nAyo kita hitung:\n\n```js run\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n\nfunction bench(f) {\n  let date1 = new Date(0);\n  let date2 = new Date();\n\n  let start = Date.now();\n  for (let i = 0; i < 100000; i++) f(date1, date2);\n  return Date.now() - start;\n}\n\nalert( 'Time of diffSubtract: ' + bench(diffSubtract) + 'ms' );\nalert( 'Time of diffGetTime: ' + bench(diffGetTime) + 'ms' );\n```\n\nWow! Menggunakan `getTime()` lebih cepat! itu karena disana tidak terdapat perubahan tipe, itu akan membuat mesinnya lebih mudah dalam mengoptimasi.\n\nOke, kita punya sesuatu. Tapi itu bukanlah sebuah pengujian kemampuan yang bagus.\n\nBayangkan itu pada saat menjalankan `bench(diffSubtract)` CPU nya sedang melakukan sesuatu yang lain, dan itu mengambil sumberdaya nya. Dan pada saat menjalankan `bench(diffGetTime)` pekerjaanya telah selesai.\n\nSebuah skenario nyata untuk Sistem Operasi multi-proses yang modern.\n\nSebagai sebuah hasil, pengujian kemampuan pertama mempunyai sedikit sumberdaya CPU daripada yang kedua. Itu mungkin akan mengakibatkan hasil menjadi keliru.\n\n**Untuk pengujian yang lebih dapat diandalkan, seluruh pengujian harus dijalankan beberapa kali.**\n\nContoh, seperti ini:\n\n```js run\nfunction diffSubtract(date1, date2) {\n  return date2 - date1;\n}\n\nfunction diffGetTime(date1, date2) {\n  return date2.getTime() - date1.getTime();\n}\n\nfunction bench(f) {\n  let date1 = new Date(0);\n  let date2 = new Date();\n\n  let start = Date.now();\n  for (let i = 0; i < 100000; i++) f(date1, date2);\n  return Date.now() - start;\n}\n\nlet time1 = 0;\nlet time2 = 0;\n\n*!*\n// jalankan bench(upperSlice) dan bench(upperLoop) setiap 10 kali bergantian\nfor (let i = 0; i < 10; i++) {\n  time1 += bench(diffSubtract);\n  time2 += bench(diffGetTime);\n}\n*/!*\n\nalert( 'Total time for diffSubtract: ' + time1 );\nalert( 'Total time for diffGetTime: ' + time2 );\n```\n\nMesin Javascript yang modern mulai menggunakan optimasi yang tinggi hanya untuk \"hot code\" yang dieksekusi beberapa kali (tidak butuh untuk optimasi hal yang jarang dieksekusi). Jadi, dalam contoh diatas, eksekusi pertama tidak benar-benar di optimasi. Kita mungkin butuh menambah sebuah pemanasan:\n\n```js\n// ditambahkan untuk \"memanaskan\" terlebih dahulu perulangan utama\nbench(diffSubtract);\nbench(diffGetTime);\n\n// sekarang benchmark\nfor (let i = 0; i < 10; i++) {\n  time1 += bench(diffSubtract);\n  time2 += bench(diffGetTime);\n}\n```\n\n```warn header=\"Berhati-hati saat melakukan microbenchmarking/pengujian kemampuan micro\"\nMesin Javascript modern melakukan banyak optimasi. mereka mungkin merekayasa hasil dari \"test buatan\" dibandingkan dengan \"pemakaian normal\", terutama ketika kita mengukur kemampuan sesuatu yang sangat kecil, seperti bagaimana operator bekerja, atau fungsi bawaan. Jadi jika kamu sangat serius ingin mengerti tentang performansi, maka pelajarilah bagaiman mesin Javascript bekerja. dan maka kamu mungkin tidak butuh microbenchmarking sama sekali\n\nKumpulan artikel yang bagus tentang V8 bisa ditemukan di <http://mrale.ph>.\n```\n\n## Date.parse dari sebuah string\n\nMetode [Date.parse(str)](mdn:js/Date/parse) bisa membaca tanggal dari sebuah string.\n\nBentuk dari string haruslah: `YYYY-MM-DDTHH:mm:ss.sssZ`, dimana:\n\n- `YYYY-MM-DD` -- adalah tanggal: tahun-bulan-hari.\n- Karakter dari `\"T\"` digunakan sebagai pembatas.\n- `HH:mm:ss.sss` -- adalah waktu: jam, menit, detik dan milidetik.\n- Bagian opsional `'Z'` menandakan zona waktu dalam format `+-hh:mm`. Huruf tunggal `Z` menandakan UTC+0.\n\nVarian yang lebih pendek juga bisa, seperti `YYYY-MM-DD` atau `YYYY-MM` atau bahkan `YYYY`.\n\nPemanggilan `Date.parse(str)` mengolah string dalam bentuk yang diberikan dan mengembalikan timestamp (angka dalam milidetik dari 1 Januari 1970 UTC+0). Jika formatnya tidak valid, akan mengembalikan `NaN`.\n\nContoh:\n\n```js run\nlet ms = Date.parse('2012-01-26T13:51:50.417-07:00');\n\nalert(ms); // 1327611110417  (timestamp)\n```\n\nKita bisa secara instan membuat sebuah objek `new Date` dari timestamp:\n\n```js run\nlet date = new Date( Date.parse('2012-01-26T13:51:50.417-07:00') );\n\nalert(date);  \n```\n\n## Ringkasan\n\n- Tanggal dan waktu dalam Javascript direpresentasikan oleh objek [Date](mdn:js/Date). Kita tidak bisa membuat \"hanya tanggal\" atau \"hanya waktu\": objek `Date` selalu membawa keduanya. \n- Bulan dihitung dari nol (ya, Januari adalah bulan ke-nol)\n- Hari-hari di minggu di `getDay()` juga dihitung dari nol (yang mana adalah minggu).\n- `Date` mengkoreksi sendiri secara otomatis ketika komponen diluar jangkauan di-set. Bagus unduk menambahkan/mengurangi hari/bulan/jam.\n- Tanggal bisa dikurangi, memberikan perbedaannya dalam milidetik. Itu karena `Date` menjadi timestamp ketika diubah menjadi angka.\n- Gunakan `Date.now()` untuk mendapatkan timestamp dengan cepat.\n\nPerhatikan tidak seperti sistem lainnya, timestamp didalam Javascript adalah dalam milidetik, bukan dalam detik.\n\nTerkadang kita ingin pengukuran yang lebih teliti. Javascript sendiri tidak mendukung cara untuk mengukur waktu didalam microdetik (1 juta dalam satu detik), tapi kebanyakan lingkungan menyediakannya. Contoh, peramban punya [performance.now()](mdn:api/Performance/now) yang memberikan angka milidetik dari awal halaman dimuat dengan ketepatan microdetik (3 angka setelah titik):\n\n```js run\nalert(`Loading started ${performance.now()}ms ago`);\n// Sesuatu seperti: \"Loading started 34731.26000000001ms ago\"\n// .26 adalah microdetik (260 microdetik)\n// lebih dari 3 angka setelah titik desimal adalah presisi error, tapi hanya 3 yang benar\n```\n\nNode.js punya modul `microtime` dan cara lainnya. Secara teknis, hampir kebanyakan perangkat dan environment memperbolehkan untuk mendapatkan presisi, itu hanya bukan didalam `Date`."
  },
  {
    "path": "1-js/05-data-types/12-json/1-serialize-object/solution.md",
    "content": "\n\n```js\nlet user = {\n  name: \"John Smith\",\n  age: 35\n};\n\n*!*\nlet user2 = JSON.parse(JSON.stringify(user));\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/05-data-types/12-json/1-serialize-object/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Ubah objek menjadi JSON dan sebaliknya\n\nUbahlah `user` menjadi JSON dan kemudian baca kembali menjadi variabel lain.\n\n```js\nlet user = {\n  name: \"John Smith\",\n  age: 35\n};\n```\n"
  },
  {
    "path": "1-js/05-data-types/12-json/2-serialize-event-circular/solution.md",
    "content": "\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  occupiedBy: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room\n};\n\nroom.occupiedBy = meetup;\nmeetup.self = meetup;\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  return (key != \"\" && value == meetup) ? undefined : value;\n}));\n\n/* \n{\n  \"title\":\"Conference\",\n  \"occupiedBy\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n\nDisini kita juga perlu untuk menguji `key==\"\"` untuk tidak memasukkan panggilan pertama dimana properti tersebut normal ketika `value` adalah `meetup`.\n\n"
  },
  {
    "path": "1-js/05-data-types/12-json/2-serialize-event-circular/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tidak memasukkan referensi balik\n\nDalam kasus-kasus sederhana tentang referensi sirkular, kita bisa tidak memasukkan sebuah properti tertentu dari proses serialisasi berdasarkan namanya.\n\nNamun terkadang kita tidak bisa menggunakan nama saja, sebagaimana bisa saja properti tersebut menggunakan referensi sirkular dan (berfungsi sebagai) properti normal. Jadi kita bisa memeriksa properti berdasarkan nilainya.\n\nTulis fungsi `replacer` untuk me-*stringify* semuanya, tetapi menghilangkan propertii yang me-referensi ke `meetup`:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  occupiedBy: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room\n};\n\n*!*\n// referensi-referensi sirkular\nroom.occupiedBy = meetup;\nmeetup.self = meetup;\n*/!*\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  /* kodemu */\n}));\n\n/* hasil yang diharapkan yakni:\n{\n  \"title\":\"Conference\",\n  \"occupiedBy\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n"
  },
  {
    "path": "1-js/05-data-types/12-json/article.md",
    "content": "# Metode JSON, toJSON\n\nAnggap saja kita memiliki sebuah objek yang kompleks, dan kita ingin mengonversinya menjadi sebuah *string*, mengirimnya melalui sebuah jaringan atau hanya menghasilkan *string* tersebut untuk tujuan pencatatan.\n\nSecara alami, *string* seperti contoh di atas seharusnya sudah termasuk semua properti-properti penting di dalamnya.\n\nKita bisa mengimplementasikan konversi tersebut seperti ini:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n\n*!*\n  toString() {\n    return `{name: \"${this.name}\", age: ${this.age}}`;\n  }\n*/!*\n};\n\nalert(user); // {name: \"John\", age: 30}\n```\n\n...Tetapi dalam proses pengembangan, properti-properti baru ditambahkan, properti-properti yang lama diganti nama baru dan dipindahkan. Memperbarui seperti `toStrong` setiap saat bisa jadi sangat menyebalkan. Kita bisa coba untuk memberi properti-properti tersebut perulangan dalam proses pengembangan, tetapi apa yang terjadi jika objeknya kompleks dan memiliki objek yang bersarang (*nested*) dalam propertinya? Kita pastinya perlu untuk mengimplementasikan konversinya juga.\n\nUntungnya, (kita) tak perlu untuk menulis kode untuk menangani semua hal ini. Tugas tersebut sudah terpecahkan solusinya.\n\n## JSON.stringify\n\n[JSON](http://en.wikipedia.org/wiki/JSON) (*JavaScript Object Notation*) adalah sebuah format umum yang merepresentasikan nilai-nilai dan objek. JSON dideskripsikan sebagaimana dalam standar [RFC 4627](http://tools.ietf.org/html/rfc4627). Awalnya JSON dibuat untuk JavaScript, tapi banyak bahasa pemrograman lain memiliki *library* untuk menangani JSON juga. Oleh karena itu, kini jadi mudah untuk menggunakan JSON untuk tujuan pertukaran data ketika klien menggunakan JavaScript dan server ditulis menggunakan bahasa pemrograman Ruby/PHP/Java/apapun itu.\n\nJavaScript menyediakan metode-metode seperti:\n\n- `JSON.stringify` untuk mengoversi objek menjadi JSON.\n- `JSON.parse` untuk mengonversi balik JSON menjadi sebuah objek.\n\nFor instance, here we `JSON.stringify` a student:\n```js run\nlet student = {\n  name: 'John',\n  age: 30,\n  isAdmin: false,\n  courses: ['html', 'css', 'js'],\n  wife: null\n};\n\n*!*\nlet json = JSON.stringify(student);\n*/!*\n\nalert(typeof json); // we've got a string!\n\nalert(json);\n*!*\n/* JSON-encoded object:\n{\n  \"name\": \"John\",\n  \"age\": 30,\n  \"isAdmin\": false,\n  \"courses\": [\"html\", \"css\", \"js\"],\n  \"wife\": null\n}\n*/\n*/!*\n```\n\nMetode `JSON.stringify(student)` mengambil objek dan mengonversikan objek tersebut menjadi sebuah *string*.\n\nHasil *string* `json` disebut sebagai sebuah objek *JSON-encoded* atau *serialized* atau *stringified* atau *marshalled*. Kita kini siap utnuk mengirimnya melalui jaringan atau menyimpannya ke sebuah penyimpanan data.\n\nMohon diingat bahwa sebuah objek *JSON-encoded* memiliki beberapa perbedaan penting dari objek secara harfiah:\n\n- *String* menggunakan tanda kutip. Dalam JSON tidak menggunakan tanda petik atau *backtick*. Jadi `'John'` menjadi `\"John\"`.\n- Nama-nama properti objek diberi tanda kutip juga. Hal ini wajib dilakukan. Jadi `age:30` menjadi `\"age\":30`.\n\n`JSON.stringify` bisa juga bisa diterapkan ke (tipe data) *primitive*.\n\nJSON mendukung tipe-tipe data berikut ini:\n\n- Objek `{ ... }`\n- *Array* `[ ... ]`\n- *Primitive*:\n    - *string*,\n    - angka (*number*),\n    - nilai-nilai *boolean* `true/false`,\n    - `null`.\n\nSebagai contoh:\n\n```js run\n// sebuah number(angka) dalam JSON hanyalah sebuah number\nalert( JSON.stringify(1) ) // 1\n\n// sebuah string dalam JSON tetaplah sebuah string, namun diberi tanda kutip\nalert( JSON.stringify('test') ) // \"test\"\n\nalert( JSON.stringify(true) ); // true\n\nalert( JSON.stringify([1, 2, 3]) ); // [1,2,3]\n```\n\nJSON adalah spessifikasi yang hanya terdiri dari data dan tidak terlekat bahasa pemrograman tertentu (*data-only language-independent*), jadi beberapa properti objek yang spesifik pada JavaScript akan dilewati oleh `JSON.stringify`.\n\nProperti-properti objek yang spesifik pada JavaScript tersebut yakni:\n\n- Properti fungsi (metode-metode).\n- Properti simbolis.\n- Propert yang menyimpan `undefined`.\n\n```js run\nlet user = {\n  sayHi() { // diabaikan\n    alert(\"Hello\");\n  },\n  [Symbol(\"id\")]: 123, // ignored\n  something: undefined // diabaikan\n};\n\nalert( JSON.stringify(user) ); // {} (objek kosong)\n```\n\nBiasanya hal tersebut tidak masalah. Jika itu tidak kita inginkan, kita akan melihat bagaimana cara untuk meng-kustomisasi proses tersebut.\n\nHal bagusnya adalah objek-objek yang *nested* secara otomatis didukung dan dikonversikan.\n\nContohnya:\n\n```js run\nlet meetup = {\n  title: \"Conference\",\n*!*\n  room: {\n    number: 23,\n    participants: [\"john\", \"ann\"]\n  }\n*/!*\n};\n\nalert( JSON.stringify(meetup) );\n/* Keseluruhan struktur di-stringify:\n{\n  \"title\":\"Conference\",\n  \"room\":{\"number\":23,\"participants\":[\"john\",\"ann\"]},\n}\n*/\n```\n\nBatasan penting: tidak boleh ada rujukan/referensi yang sirkular/berputar-putar.\n\nContohnya:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [\"john\", \"ann\"]\n};\n\nmeetup.place = room;       // meetup mereferensikan room \nroom.occupiedBy = meetup; // room mereferensikan meetup\n\n*!*\nJSON.stringify(meetup); // Error: Converting circular structure to JSON\n*/!*\n```\n\nDisini, konversi gagal, karena adanya referensi yang memutar/sirkular: `room.occupiedBy` mereferensikan ke `meetup`, dan `meetup.place` mereferensikan ke `room`:\n\n![](json-meetup.svg)\n\n\n## Mengecualikan dan mengubah: *replacer*\n\nSintaks lengkap dari `JSON.stringify` adalah:\n\n```js\nlet json = JSON.stringify(value[, replacer, space])\n```\n\n*value*\n: Sebuah nilai untuk di-*encode*.\n\n*replacer*\n: *Array* properti untuk di-*encode* atau sebuah fungsi pemetaan `function(key, value)`.\n\n*space*\n: Jumlah ruang yang digunakan untuk proses *formatting*.\n\nSeringkali, `JSON.stringify` digunakan dengan hanya sebuah argumen pertama. Tapi jika kita perlu untuk menyetel dengan baik proses pergantian tersebut, seperti menyaring referensi-referensi sirkular, kita dapat menggunakan argumen kedua dari `JSON.stringify`.\n\nJika kita mengoper sebuah *array* properti ke proses tersebut, hanya properti-properti berikut ini yang akan di-*encode*.\n\nSebagai contoh:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup mereferensikan room\n};\n\nroom.occupiedBy = meetup; // room mereferensikan meetup\n\nalert( JSON.stringify(meetup, *!*['title', 'participants']*/!*) );\n// {\"title\":\"Conference\",\"participants\":[{},{}]}\n```\n\nKini kita bisa jadi terlalu ketat (dalam mendeklarasikan). Daftar properti tersbeut diterapkan ke keseluruhan struktur objek. Jadi objek-objek dalam `participants` kosong, karena `name` tidak ada dalam daftar.\n\nMari memasukkan semua properti ke dalam daftar kecuali properti `room.occupiedBy` yang dapat menyebabkan referensi sirkular:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup mereferensikan room\n};\n\nroom.occupiedBy = meetup; // room mereferensikan meetup\n\nalert( JSON.stringify(meetup, *!*['title', 'participants', 'place', 'name', 'number']*/!*) );\n/*\n{\n  \"title\":\"Conference\",\n  \"participants\":[{\"name\":\"John\"},{\"name\":\"Alice\"}],\n  \"place\":{\"number\":23}\n}\n*/\n```\n\nKini semuanya kecuali `occupiedBy` sudah di-serialisasi. Tapi daftar propertinya masih cukup panjang.\n\nUntungnya, kita bisa menggunakan sebuah fungsi ketimbang sebuah *array* sebagai `replacer`.\n\nFungsi tersebut akan dipanggil pada setiap pasangan `(key, value)` dan mengembalikan nilai \"replaced\", yang mana akan digunakan dan bukan nilai aslinya. Atau `undefined` jika nilai tersebut diatur agar dilewatkan.\n\nDalam kasus kita, kita bisa mengembalikan   `value` \"as is\" untuk semua hal kecuali `occupiedBy`. Untuk mengabaikan `occupiedBy`, kode berikut ini mengembalikan `undefined`:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  participants: [{name: \"John\"}, {name: \"Alice\"}],\n  place: room // meetup mereferensikan room\n};\n\nroom.occupiedBy = meetup; // room mereferensikan meetup\n\nalert( JSON.stringify(meetup, function replacer(key, value) {\n  alert(`${key}: ${value}`);\n  return (key == 'occupiedBy') ? undefined : value;\n}));\n\n/* pasangan key:value yang menuju ke replacer:\n:             [object Object]\ntitle:        Conference\nparticipants: [object Object],[object Object]\n0:            [object Object]\nname:         John\n1:            [object Object]\nname:         Alice\nplace:        [object Object]\nnumber:       23\noccupiedBy: [object Object]\n*/\n```\n\nMohon diingat, bahwa fungsi `replacer` mendapatkan setiap pasang *key/value* termasuk objek-objek *nested* dan *item* dalam *array*. Hal tersebut dapat diterapakn berulang (*recursive*). Nilai dari `this` dalam `replacer` adalah objek yang mengendung properti yang sekarang.\n\nPanggilan pertama itu khusus. Panggilan pertama tersebut dibuat menggunakan sebuah \"wrapper object\" yang khusus: `{\"\": meetup}`. Dengan kata lain, pasangan `(key, value)` pertama memiliki sebuah kunci kosong, dan nilainya adalah objek sasaran seutuhnya. Itulah mengapa baris pertama dalam contoh di atas adalah `\":[object Object]\"`.\n\nIde tersebut adalah untuk menyediakan sebanyak mungkin kemampuan pada `replacer`: ide tersebut punya sebuah kesempatan untuk menganalisis dan menggantikan/melewatkan hingga keseluruhan objek jika perlu.\n\n\n## Proses *Formatting*: *space*\n\nArgumen ke-tiga dari `JSON.stringify(value, replacer, space)` adalah jumlah ruang (*space*) yang digunakan untuk *formatting* yang apik.\n\nSebelumnya, semua objek yang di-*stringify* tidak memiliki ruang tambahan. Hal tersebut tidak masalah jika kita ingin mengirim sebuah objek melalui sebuah jaringan. Argumen `space` digunakan secara ekslusif demi sebuah hasil yang apik.\n\nDi sini `space = 2` memberitahukan JavaScript untuk menunjukkan objek-objek *nested* pada beberapa baris, dengan kedalaman (*indentation*) sebanyak 2 ruang (*space*) di dalam sebuah objek:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 25,\n  roles: {\n    isAdmin: false,\n    isEditor: true\n  }\n};\n\nalert(JSON.stringify(user, null, 2));\n/* indent dengan dua spasi:\n{\n  \"name\": \"John\",\n  \"age\": 25,\n  \"roles\": {\n    \"isAdmin\": false,\n    \"isEditor\": true\n  }\n}\n*/\n\n/* untuk JSON.stringify(user, null, 4) hasilnya kan lebih menjorok ke dalam:\n{\n    \"name\": \"John\",\n    \"age\": 25,\n    \"roles\": {\n        \"isAdmin\": false,\n        \"isEditor\": true\n    }\n}\n*/\n```\n\nParameter `space` digunakan hanya untuk pencatatan dan tujuan-tujuan yang bertujuan menghasilkan *output* yang apik.\n\n## \"toJSON\" khusus\n\nSeperti `toString` untuk konversi *string*, sebuah objek dapat menyediakan metode `toJSON` untuk konversi ke JSON. `JSON.stringify` secara otomatis akan memanggil metode tersebut jika tersedia.\n\nContohnya:\n\n```js run\nlet room = {\n  number: 23\n};\n\nlet meetup = {\n  title: \"Conference\",\n  date: new Date(Date.UTC(2017, 0, 1)),\n  room\n};\n\nalert( JSON.stringify(meetup) );\n/*\n  {\n    \"title\":\"Conference\",\n*!*\n    \"date\":\"2017-01-01T00:00:00.000Z\",  // (1)\n*/!*\n    \"room\": {\"number\":23}               // (2)\n  }\n*/\n```\n\nDi sini kita bisa lihat bahwa `date` `(1)` menjadi sebuah *string*. Itu karena semua tanggal memiliki sebuah metode `toJSON` yang sudah *built-in* yang mana mengembalikan *string* seperti itu.\n\nKini mari menambahkan sebuah `toJSON` khusus untuk objek kita `room` `(2)`:\n\n```js run\nlet room = {\n  number: 23,\n*!*\n  toJSON() {\n    return this.number;\n  }\n*/!*\n};\n\nlet meetup = {\n  title: \"Conference\",\n  room\n};\n\n*!*\nalert( JSON.stringify(room) ); // 23\n*/!*\n\nalert( JSON.stringify(meetup) );\n/*\n  {\n    \"title\":\"Conference\",\n*!*\n    \"room\": 23\n*/!*\n  }\n*/\n```\n\nSeperti yang kita lihat, `toJSON` digunakan untuk memanggil langsung `JSON.stringify(room)` serta ketikan `room` bersarang (*nested*) dalam objek lain yang ter-*encode*.\n\n\n## JSON.parse\n\nUntuk men-*decode* sebuah *string* JSON, kita memerlukan sebuah metode lain bernama [JSON.parse](mdn:js/JSON/parse).\n\nSintaksnya:\n```js\nlet value = JSON.parse(str, [reviver]);\n```\n\n*str*\n: *string* JSON untuk di-*parse*.\n\n*reviver*\n: Fungsi opsional (*key,value*) yang akan dipanggil setiap pasang `(key, value)` dan dapat mengubah nilai.\n\nSebagai contoh:\n\n```js run\n// array yang di-stringify\nlet numbers = \"[0, 1, 2, 3]\";\n\nnumbers = JSON.parse(numbers);\n\nalert( numbers[1] ); // 1\n```\n\nAtau untuk objek-objek yang *nested*:\n\n```js run\nlet userData = '{ \"name\": \"John\", \"age\": 35, \"isAdmin\": false, \"friends\": [0,1,2,3] }';\n\nlet user = JSON.parse(userData);\n\nalert( user.friends[1] ); // 1\n```\n\nJSON bisa saja menjadi kompleks seiring jika perlu, objek-objek dan *array* bisa sudah termasuk objek-objek lain serta *array* lain. Namun, mereka (objek dan *array* lain tersebut) harus mematuhi format JSON yang sama.\n\nBerikut ini adalah beberapa kesalahan umum saat penulisan langsung JSON (terkadang kita harus menuliskannya untuk tujuan *debugging*):\n\n```js\nlet json = `{\n  *!*name*/!*: \"John\",                     // kesalahan: nama properti tanpa tanda kutip\n  \"surname\": *!*'Smith'*/!*,               // kesalahan: nilai menggunakan tanda petik (harus tanda kutip)\n  *!*'isAdmin'*/!*: false                  // kesalahan: menggunakan tanda petik pada key (harus tanda kutip)\n  \"birthday\": *!*new Date(2000, 2, 3)*/!*, // kesalahan: tidak boleh ada \"new\", hanya berupa nilai saja\n  \"friends\": [0,1,2,3]              // tidak ada kesalahan\n}`;\n```\n\nSelaain itu semua, JSON tidak mendukung komentar. Menambahkan sebuah komentar ke JSON akan membuat JSON tersebut tidak valid.\n\nTerdapat format lain yang dinamakan [JSON5](http://json5.org/), yang mengizinkan *key* tanpa tanda kutip, adanya komentar dan lain-lain. Tapi ini adalah *library* yang berdiri sendiri, tidak terdapat dalam spesifikasi bahasa pemrograman.\n\nJSON biasa memang seketat itu bukan karena para pengembangnya malas, tetapi agar implementasinya mudah, dapat diandalkan dan cepat saat proses *parsing* algoritma.\n\n## Menggunakan *reviver*\n\nBayangkan, kita mendapat sebuah objek `meetup` yang telah di-*stringify* dari server.\n\nObjek tersebut akan terlihat seperti ini:\n\n```js\n// title: (judul meetup), date: (tanggal meetup)\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n```\n\n...Dan sekarang kita perlu untuk men-deserialisasi (*deserialize*) objek itu, untuk mengembalikannya menjadi objek JavaScript.\n\nMari lakukan dengan memamnggil `JSON.parse`:\n\n```js run\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n\nlet meetup = JSON.parse(str);\n\n*!*\nalert( meetup.date.getDate() ); // Error!\n*/!*\n```\n\nUpps! Ada error!\n\nNilai dari `meetup.date` adalah sebuah *string*, bukan sebuah objek `Date`. Bagaimana cara `JSON.parse` tahu bahwa ia harus mengubah *string* itu menjadi sebuah `Date`?\n\nMari oper ke `JSON.parse` fungsi yang digunakan lagi sebagai argumen kedua, yang mengembalikan semua nilai \"as is\", tetapi `date` akan menjadi sebuah objek `Date` dengan format yang benar:\n\n```js run\nlet str = '{\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"}';\n\n*!*\nlet meetup = JSON.parse(str, function(key, value) {\n  if (key == 'date') return new Date(value);\n  return value;\n});\n*/!*\n\nalert( meetup.date.getDate() ); // kini bekerja!\n```\n\nBy the way, that works for nested objects as well:\n\n```js run\nlet schedule = `{\n  \"meetups\": [\n    {\"title\":\"Conference\",\"date\":\"2017-11-30T12:00:00.000Z\"},\n    {\"title\":\"Birthday\",\"date\":\"2017-04-18T12:00:00.000Z\"}\n  ]\n}`;\n\nschedule = JSON.parse(schedule, function(key, value) {\n  if (key == 'date') return new Date(value);\n  return value;\n});\n\n*!*\nalert( schedule.meetups[1].date.getDate() ); // berhasil bekerja!\n*/!*\n```\n\n\n\n## Kesimpulan\n\n- JSON adalah sebuah format data yang memiliki standar dan *library*-nya sendiri untuk sebagian besar bahasa-bahasa pemrograman.\n- JSON mendukung objek-objek polos, *array*, *string*, angka, *boolean*, dan `null`.\n- JavaScript menyediakan metode-metode [JSON.stringify](mdn:js/JSON/stringify) untuk men-serialisasi objek menjadi JSON serta [JSON.parse](mdn:js/JSON/parse) untuk menbaca objek dari JSON.\n- Kedua metode tersebut mendukung fungsi-fungsi pengubah untuk proses pembacaan (*reading*)/penulisan (*writing*) yang cerdas.\n- Jika sebuah objek memiliki `toJSON`, lalu metode tersebut akan dipanggil oleh `JSON.stringify`.\n"
  },
  {
    "path": "1-js/05-data-types/index.md",
    "content": "# Tipe data\n\nStruktur data lainnya dan pembelajaran lebih dalam tentang tipe-tipe.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/01-sum-to/solution.md",
    "content": "Solusi menggunakan perulangan:\n\n```js run\nfunction sumTo(n) {\n  let sum = 0;\n  for (let i = 1; i <= n; i++) {\n    sum += i;\n  }\n  return sum;\n}\n\nalert( sumTo(100) );\n```\n\nSolusi menggunakan rekursi:\n\n```js run\nfunction sumTo(n) {\n  if (n == 1) return 1;\n  return n + sumTo(n - 1);\n}\n\nalert( sumTo(100) );\n```\n\nSolusi menggunakan rumus: `sumTo(n) = n*(n+1)/2`:\n\n```js run\nfunction sumTo(n) {\n  return n * (n + 1) / 2;\n}\n\nalert( sumTo(100) );\n```\n\nCatatan. Biasanya, rumus adalah solusi tercepat. Itu hanya menggunakan 3 operasi untuk angka `n` apapun. Matematika mambantu!\n\nvarian perulangan adalah yang kedua dalam hal waktu. Di varian rekusif dan perulangan kita menambahkan angka yang sama. Tapi rekursi melibatkan pemanggilan bercabang dan manajemen tumpukan eksekusi. Itu juga memakan sumberdaya, jadi itu lebih lambat.\n\nCatatan+. Beberapa mesin mendukung optimasi \"tail call\": jika sebuah pemanggilan rekursi adalah yang paling terakhir didalam fungsi (seperti dalam `sumTo` diatas), maka fungsi terluar tidak butuh untuk melanjutkan eksekusi, jadi mesinnya tidak akan mengingat konteks dari eksekusi. Itu akan menghilangkan beban didalam memori, jadi menghitung `sumTo(100000)` menjadi mungkin. Tapi jika mesin Javascript tidak mendukung optimasi tail call (kebanyakan tidak), disana akan terdapat error: \"maximum stack size exceeded\", karena disana biasanya terdapat batasan dalam total ukuran stack/penumpukan.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/01-sum-to/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Tambahkan seluruh angka sampai angka yang diberikan\n\nTulis sebuah fungsi `sumTo(n)` yang mengkalkulasikan penambahan dari angka `1 + 2 + ... + n`.\n\nContoh:\n\n```js no-beautify\nsumTo(1) = 1\nsumTo(2) = 2 + 1 = 3\nsumTo(3) = 3 + 2 + 1 = 6\nsumTo(4) = 4 + 3 + 2 + 1 = 10\n...\nsumTo(100) = 100 + 99 + ... + 2 + 1 = 5050\n```\n\nBuatlah jawaban dengan 3 varian:\n\n1. Gunakan perulangan\n2. Gunakan rekursi, karena `sumTo(n) = n + sumTo(n-1)` untuk `n > 1`.\n3. Gunakan rumus [arithmetic progression](https://en.wikipedia.org/wiki/Arithmetic_progression).\n\nContoh dari hasil:\n\n```js\nfunction sumTo(n) { /*... kodemu ... */ }\n\nalert( sumTo(100) ); // 5050\n```\n\nCatatan. Varian solusi mana yang lebih cepat? yang lebih lambat? kenapa?\n\nCatatan+. Bisakah kita gunakan rekursi untuk menghitung `sumTo(100000)`?\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/02-factorial/solution.md",
    "content": "Secara definisi, sebuah faktorial adalah `n!` bisa ditulis juga sebagai `n * (n-1)`.\n\nDengan kata lain, hasil dari `factorial(n)` bisa juga dikalkulasikan sebagai `n` dikalikan dengan hasil dari `factorial(n-1)`. Dan pemanggilan untuk `n-1` bisa secara rekursi menurun, dan terus menurun sampai `1`.\n\n```js run\nfunction factorial(n) {\n  return (n != 1) ? n * factorial(n - 1) : 1;\n}\n\nalert( factorial(5) ); // 120\n```\n\nBasis dari rekursi adalah nilai `1`. Kita jika bisa membuat basis `0` disini, tidak akan berpengaruh banyak, tapi memberikan satu lagi tingkat rekursi:\n\n```js run\nfunction factorial(n) {\n  return n ? n * factorial(n - 1) : 1;\n}\n\nalert( factorial(5) ); // 120\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/02-factorial/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Kalkulasikan faktorial\n\n[factorial](https://en.wikipedia.org/wiki/Factorial) dari sebuah angka natural adalah sebuah angka yang dikalikan dengan `\"angka minus satu\"`, lalu dengan `\"angka minus dua\"`, dan terus sampai `1`. Faktorial dari `n` di notasikan sebagai `n!`.\n\nKita bisa menulis definisi dari faktorial seperti ini:\n\n```js\nn! = n * (n - 1) * (n - 2) * ...*1\n```\n\nNilai dari faktorial untuk `n` yang berbeda:\n\n```js\n1! = 1\n2! = 2 * 1 = 2\n3! = 3 * 2 * 1 = 6\n4! = 4 * 3 * 2 * 1 = 24\n5! = 5 * 4 * 3 * 2 * 1 = 120\n```\n\nTugasnya adalah untuk menulis sebuah fungsi `factorial(n)` yang mengkalkulasikan `n!` menggunakan pemanggilan rekursi.\n\n```js\nalert( factorial(5) ); // 120\n```\n\nCatatan. Petunjuk: `n!` bisa juga ditulis sebagai `n * (n-1)!`, Contoh: `3! = 3*2! = 3*2*1! = 6`\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/solution.md",
    "content": "Solusi pertama kita harus coba adalah rekursi.\n\nAngka fibonacci adalah rekursi dari definisinya:\n\n```js run\nfunction fib(n) {\n  return n <= 1 ? n : fib(n - 1) + fib(n - 2);\n}\n\nalert( fib(3) ); // 2\nalert( fib(7) ); // 13\n// fib(77); // akan sangat lambat!\n```\n\n...Tapi untuk nilai besar dari `n` akanlah sangat lambat. Contoh, `fib(77)` mungkin akan memberatkan mesinnya dan untuk beberapa saat akan menggunakan seluruh sumberdaya CPU.\n\nItu karena fungsinya memanggil terlalu banyak pemanggilan. Nilai yang sama akan terus di evaluasi lagi dan lagi.\n\nContoh, kita lihat satu potongan dari kalkulasi untuk `fib(5)`:\n\n```js no-beautify\n...\nfib(5) = fib(4) + fib(3)\nfib(4) = fib(3) + fib(2)\n...\n```\n\nDisini kita bisa melihat nilai dari `fib(3)` dibutuhkan untuk `fib(5)` dan `fib(4)`. Jadi `fib(3)` akan dipanggil dan dievaluasi dua kali secara bergantian.\n\nIni adalah pohon rekursi penuh:\n\n![fibonacci recursion tree](fibonacci-recursion-tree.svg)\n\nKita dengan jelas bisa memperhatikan bahwa `fib(3)` dievaluasi dua kali dan `fib(2)` di evaluasi tiga kali. Total dari komputasi akan terus membesar secara cepat lebih dari `n`, membuatnya besar sekali bahkan untuk `n=77`.\n\nKita bisa mengoptimasinya dengan mengingat nilai yang telah dievaluasi: jika sebuah nilai katakan `fib(3)` dikalkulasi sekali, lalu kita bisa menggunakan nilainya lagi di komputasi selanjutnya.\n\nVarian lainnya haruslah menyerah dengan rekursi dan menggunakan algoritma lain yang benar-benar berbeda.\n\nDaripada memulai dari `n` ke nilai yang dibawahnya, kota bisa membuat perulangan dimulai dari `1` dan `2`, lalu mendapatkan `fib(3)` sebagai nilai penambahan mereka, lalu `fib(4)` sebagai nilai penambahan dua bilangan sebelumnya, lalu `fib(5)` dan terus naik, sampai itu mencapai nilai yang dibutuhkan. Untuk setiap langkah kita hanya membutuhkan nilai dari kedua bilangan sebelumnya.\n\nIni adalah detail langkah dari algoritma baru.\n\nMulai:\n\n```js\n// a = fib(1), b = fib(2), nilai-nilai ini adalah definisi dari 1\nlet a = 1, b = 1;\n\n// get c = fib(3) sebagai penambahan mereka\nlet c = a + b;\n\n/* sekarang kita punya fib(1), fib(2), fib(3)\na  b  c\n1, 1, 2\n*/\n```\n\nsekarang kita ingin mendapatkan `fib(4) = fib(2) + fib(3)`.\n\nLalu kita ubah variabelnya: `a,b` akan mendapatkan `fib(2),fib(3)`, dan `c` akan mendapatkan penambahan mereka:\n\n```js no-beautify\na = b; // now a = fib(2)\nb = c; // now b = fib(3)\nc = a + b; // c = fib(4)\n\n/* sekarang kita punya urutannya:\n   a  b  c\n1, 1, 2, 3\n*/\n```\n\nLangkah selanjutnya akan memberikan urutan angka lainnya:\n\n```js no-beautify\na = b; // now a = fib(3)\nb = c; // now b = fib(4)\nc = a + b; // c = fib(5)\n\n/* sekarang urutannya adalah (satu angka lagi):\n      a  b  c\n1, 1, 2, 3, 5\n*/\n```\n\n...Dan seterusnya sampai kita mendapatkan nilai yang dibutuhkan. Itu lebih cepat daripada rekursi dan tidak melibatkan duplikasi komputasi\n\nSemua kodenya:\n\n```js run\nfunction fib(n) {\n  let a = 1;\n  let b = 1;\n  for (let i = 3; i <= n; i++) {\n    let c = a + b;\n    a = b;\n    b = c;\n  }\n  return b;\n}\n\nalert( fib(3) ); // 2\nalert( fib(7) ); // 13\nalert( fib(77) ); // 5527939700884757\n```\n\nPerulangak dimulai dari `i=3`, karena yang urutan nilai pertama dan kedua sudah dikode (hard-coded) kedalam variabel `a=1`, `b=1`.\n\nPendekatan ini dipanggil dengan [dynamic programming bottom-up](https://en.wikipedia.org/wiki/Dynamic_programming).\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/03-fibonacci-numbers/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Angka fibonacci\n\nUrutan dari [Fibonacci numbers](https://en.wikipedia.org/wiki/Fibonacci_number) mempunyai rumus  <code>F<sub>n</sub> = F<sub>n-1</sub> + F<sub>n-2</sub></code>. Dengan kata lain, angka selanjutnya adalah penambahan dari dua angka sebelumnya.\n\nDua angka pertama adalah `1`, lalu `2(1+1)`, lalu `3(1+2)`, `5(2+3)` dan seterusnya: `1, 1, 2, 3, 5, 8, 13, 21...`.\n\nAngka fibonnaci adalah angka yang terkait kedalam [Golden ratio](https://en.wikipedia.org/wiki/Golden_ratio) dan fenomena natural lainnya disekitar kita.\n\nBuat sebuah fungsi `fib(n)` yang mengembalikan angka fibonacci ke-`n-th`.\n\nContoh hasil:\n\n```js\nfunction fib(n) { /* kodemu */ }\n\nalert(fib(3)); // 2\nalert(fib(7)); // 13\nalert(fib(77)); // 5527939700884757\n```\n\nCatatan. Fungsinya haruslah cepat. Pemanggilan ke `fib(77)` haruslah tidak memakan lebih dari beberapa detik.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/solution.md",
    "content": "# solusi berbasis perulangan\n\nVarian solusi berbasis perulangan:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printList(list) {\n  let tmp = list;\n\n  while (tmp) {\n    alert(tmp.value);\n    tmp = tmp.next;\n  }\n\n}\n\nprintList(list);\n```\n\nPerhatikan bahwa kita menggunakan variabel semebtara `tmp` untuk menyusuri daftarnya. Secara teknis, malah kita bisa menggunakan parameter fungsi `list`:\n\n```js\nfunction printList(list) {\n\n  while(*!*list*/!*) {\n    alert(list.value);\n    list = list.next;\n  }\n\n}\n```\n\n...Tapi itu kurang tepat. Nanti mungkin kita ingin memperbesar fungsinya, melakukan sesuatu dengan daftarnya. Jika kita merubah `list`, maka kita akan kehilangan kemampuannya.\n\nBerbicara tentang nama variabel yang bagus, `list` disini adalah menandakan bahwa dirinya sendiri adalah list/daftar. Elemen pertama dari itu. Dan itu harus tetap seperti itu. Itu jelas dan dapat diandalkan.\n\nDari sisi lainnya, peran dari `tmp` sendiri secara eksklusif adalah daftar traversal, seperti `i` didalam perulangan `for`.\n\n# Solusi rekursif\n\nVarian rekursif dari `printList(list)` mengikuti logika yang sederhana: untuk mengeluarkan sebuah daftar kita harus mengeluatkan elemen saat ini dari `list`, lalu lakukan hal yang sama untuk `list.next`:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printList(list) {\n\n  alert(list.value); // keluarkan item yang sekarang\n\n  if (list.next) {\n    printList(list.next); // lakukan hal yang sama dengan sisa item dalam list\n  }\n\n}\n\nprintList(list);\n```\n\nSekarang mana yang lebih baik?\n\nSecara teknis, perulanganlah yang lebih efektif. Kedua varian itu melakukan hal yang sama, tapi perulangan tidak menghabiskan sumberdaya untuk pemanggilan fungsi yang bercabang.\n\nDisisi lainnya, varian rekursi lebih pendek dan terkadang lebih mudah untuk dimengerti.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/04-output-single-linked-list/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Keluarkan sebuah daftar single-linked\n\nKatakan kita punya sebuah daftar single-linked (seperti yang dideskripsikan pada bab <info:recursion>);\n\n```js\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n```\n\nTulis sebuah fungsi `printList(list)` yang mengeluarkan item dalam daftar satu per satu.\n\nBuatlah dua varian dari solusinya: gunakan perulangan dan gunakan rekursi.\n\nMana yang lebih baik: dengan rekursi atau tanpa rekursi?\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/solution.md",
    "content": "# Menggunakan rekursi\n\nLogika rekursi sedikit lebih rumit disini.\n\nPertama kita harus mengeluarkan sisa item di daftarnya dan *lalu* mengeluarkan item yang sekarang dipilih.\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printReverseList(list) {\n\n  if (list.next) {\n    printReverseList(list.next);\n  }\n\n  alert(list.value);\n}\n\nprintReverseList(list);\n```\n\n# Menggunakan perulangan\n\nVarian perulangan juga sedikit lebih rumit daripada mengeluarkannya secara langsung.\n\nTidak ada cara untuk mendapatkan nilai terakhir didalam `list` kita. Kita juga tidak bisa \"berjalan mundur\".\n\nJadi apa yang kita bisa lakukan adalah pertama susuri seluruh item secara langsung dan mengingat mereka didalam array, dan lalu mengeluarkan apa yang diingat dengan urutan terbalik:\n\n```js run\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n\nfunction printReverseList(list) {\n  let arr = [];\n  let tmp = list;\n\n  while (tmp) {\n    arr.push(tmp.value);\n    tmp = tmp.next;\n  }\n\n  for (let i = arr.length - 1; i >= 0; i--) {\n    alert( arr[i] );\n  }\n}\n\nprintReverseList(list);\n```\n\nPerhatikan baik-baik bahwa solusi rekursi melakukan hal yang sama persis: itu akan menyusuri daftar, mengingat item-itemnya didalam rantai dari pemanggulan bercabang (dalam konteks penumpukan eksekusi), dan lalu mengeluarkan hasilnya.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/05-output-single-linked-list-reverse/task.md",
    "content": "nilai penting: 5\n\n---\n\n# keluarkan item single-linked dari list dengan urutan terbalik\n\nKeluarkan item single-linked dari daftar seperti tugas sebelumnya <info:task/output-single-linked-list> namun secara terbalik.\n\nBuatlah dua solusi: menggunakan perulangan dan menggunakan rekursi.\n"
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/article.md",
    "content": "# Rekursi dan tumpukan (Recursion and stack)\n\nAyo kita kembali ke fungsi dan belajar tentanya lebih dalam lagi.\n\nTopik pertama kita adalah *rekursi*.\n\nJika programming bukanlah hal baru untukmu, maka kamu mungkin familiar dan kamu bisa melewatkan bab ini.\n\nRekursi adalah pola programming yang sangat berguna didalam situasi dimana sebuah task bisa secara natural dibagi menjadi beberapa task yang memiliki jenis yang sama, tapi lebih sederhana. Atau ketika task bisa dibuat lebih sederhana menjadi sebuah aksi ditambah varian yang lebih sederhana dari beberapa task yang serupa.\n\nketika sebuah fungsi menyelesaikan sebuah task, didalam proses itu bisa memanggila beberapa fungsi lainnya. Sebagian kasus dari ini ketika sebuah fungsi memanggil *dirinya sendiri*. Itulah yang disebut rekursi.\n\n## Cara berfikir dua arah\n\nUntuk sesuatu yang sederhana dimulai dengan -- ayo kita buat sebuah fungsi `pow(x, n)` yang menaikan `x` dengan pangkat dari `n`. Dengan kata lain, mengkalikan `x` dengan dirinya sendiri sebanyak `n` kali.\n\n```js\npow(2, 2) = 4\npow(2, 3) = 8\npow(2, 4) = 16\n```\n\nTerdapat dua cara untuk mengimplementasikan hal itu.\n\n1. Pemikiran interaktif: perulangan `for`:\n\n    ```js run\n    function pow(x, n) {\n      let result = 1;\n\n      // kalikan hasil dari x n kali didalam perulangan\n      for (let i = 0; i < n; i++) {\n        result *= x;\n      }\n\n      return result;\n    }\n\n    alert( pow(2, 3) ); // 8\n    ```\n\n2. Pemikiran rekursif: sederhanakan tugasnya dan panggil diri-sendiri:\n\n    ```js run\n    function pow(x, n) {\n      if (n == 1) {\n        return x;\n      } else {\n        return x * pow(x, n - 1);\n      }\n    }\n\n    alert( pow(2, 3) ); // 8\n    ```\n\nIngat baik-baik bagaimana varian rekursif secara dasar berbeda.\n\nKetika `pow(x, n)` dipanggil, eksekusinya dibagi menjadi dua cabang:\n\n```js\n              if n==1  = x\n             /\npow(x, n) =\n             \\       \n              else     = x * pow(x, n - 1)\n```\n\n1. Jika `n == 1`, maka semuanya menjadi tidak penting. Itulah yang dipanggil dengan *dasar* dari rekursi, karena itu langsung menghasilkan nilai yang jelas `pow(x, 1)` sama dengan `x`.\n2. Sebaliknya, kita bisa merepresentasikan `pow(x, n)` sebagai `x * pow(x, n - 1)`. Didalam matematika, satu akan ditulis <code>x<sup>n</sup> = x * x<sup>n-1</sup></code>. Ini yang dipanggil dengan *Langkah rekursif*: kita mengubah tugas/task menjadi aksi yang lebih sederhana (perkalian dengan `x`) dan sebuah pemanggilan yang lebih sederhana dari tugas/task yang sama (`pow` dengan menurunkan `n`). Langkah selanjutnya menyederhanakan itu lebih jauh sampai `n` mencapai `1`.\n\nKita bisa berkata bahwa `pow` *secara rekursif memanggil dirinya sendiri sampai `n == 1`.\n\n![recursive diagram of pow](recursion-pow.svg)\n\n\nContoh, untuk mengkalkulasi `pow(2, 4)` varian rekursi melakukan langkah-langkah ini:\n\n1. `pow(2, 4) = 2 * pow(2, 3)`\n2. `pow(2, 3) = 2 * pow(2, 2)`\n3. `pow(2, 2) = 2 * pow(2, 1)`\n4. `pow(2, 1) = 2`\n\nJadi, rekursi mengurangi sebuah pemanggilan fungsi menjadi yang lebih sederhana, dan lalu -- bahkan lebih sederhana, dan seterusnya, sampai hasilnya menjadi jelas.\n\n````smart header=\"Rekursi biasanya lebih pendek\"\nSolusi rekursi biasanya lebih pendek daripada sebuah iterasi.\n\nDisini kita bisa menulis ulang menggunakan operator konsional `?` daripada `if` untuk membuat `pow(x, n)` lebih pendek dan tetap mudah dibaca:\n\n```js run\nfunction pow(x, n) {\n  return (n == 1) ? x : (x * pow(x, n - 1));\n}\n```\n````\n\nAngka maksimal dari pemanggilan bercabang (termasuk yang pertama) dipanggil dengan *kedalaman rekursi/recursion depth*. Di kasus kita, itu akan persis `n`.\n\nMaksimal kedalaman rekursi dibatasi oleh mesin Javascript. Kita bisa berkata bahwa itu mungkin 10000, beberapa mesin bisa lebih tapi 100000 mungkin sudah diluar batas dari kebanyakan mesin. Terdapat optimasi otomatis yang membantu meringankan ini(\"optimasi tail calls\"), tapi mereka belum sepenuhnya didukung di semuanya dan hanya bekerja pada kasus yang sederhana.\n\nItu membatasi aplikasi dari rekursi, tapi itu tetaplah cukup besar. Disana terdapat task dimana cara berfikir rekursif membuat kode lebih sederhana, dan lebih mudah diperlihara.\n\n## Konteks eksekusi dan tumpukan\n\n\nSekarang ayo kita membahas bagaimana pemanggilan rekursi bekerja. Untuk itu kita akan melihat isi dari fungsi.\n\nInformasi tentang proses dari eksekusi dari sebuah fungsi yang berjalan disimpan didalam *konteks eksekusi*nya.\n\n[Execution context](https://tc39.github.io/ecma262/#sec-execution-contexts) adalah sebuah struktur data internal yang mengandung detail tentang eksekusinya dari sebuah fungsi: dimana alur kontrolnya adalah sekarang, variabel yang sekarang, nilai dari `this` (kita tidak akan mengunakan ini disini) dan beberapa detail internal lainnya.\n\nSatu pemanggilan fungsi mempunyai tepat satu konteks eksekusi yang terkait dengannya.\n\nKetika sebuah fungsi melakukan pemanggilan bercabang, Hal berikut terjadi:\n\n- Fungsi yang sekarang dihentikan sementara -- paused.\n- Konteks eksekusi yang terkait dengannya diingat dalam sebuah struktur data spesial dipanggil dengan *tumpukan konteks eksekusi*.\n- Pemanggilan bercabang dieksekusi.\n- Setelah itu selesai, eksekusi konteks yang lama diterima dari tumpukan, dan fungsi terluar dilanjutkan dari mana itu berhenti.\n\nAyo kita lihat apa yang terjadi selama pemanggilan `pow(2, 3)`.\n\n### pow(2, 3)\n\nDi awal pemanggilan `pow(2, 3)` konteks eksekusi akan menyimpan variabel: `x = 2, n = 3`, alur eksekusi berada pada baris `1` dari fungsi.\n\nKita bisa menggambarkannya seperti:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nItu ketika fungsi mulai dieksekusi. Kondisinya `n == 1` adalah false, jadi alurnya berlanjut ke cabang kedua dari `if`:\n\n```js run\nfunction pow(x, n) {\n  if (n == 1) {\n    return x;\n  } else {\n*!*\n    return x * pow(x, n - 1);\n*/!*\n  }\n}\n\nalert( pow(2, 3) );\n```\n\n\nVariabelnya juga sama, tapi barisnya berubah, jadi konteksnya sekarang:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nUntuk mengkalkulasikan `x * pow(x, n -1)`, kita perlu membuat subcall dari `pow` dengan argumen baru `pow(2, 2)`.\n\n### pow(2, 2)\n\nUntuk melakukan pemanggilan bercabang, javascript mengingat konteks eksekusi yang sekarang didalam *tumpukan konteks eksekusi*.\n\nDisini kita memanggil fungsi yang sama `pow`, tapi itu tidaklah penting. Prosesnya sama untuk semua fungsi:\n\n1. Konteks yang sekarang telah \"mengingat\" tumpukan teratas.\n2. Konteks baru dibuat untuk subcall.\n3. Ketika subcall telah selesai -- konteks sebelumnya dikeluarkan dari tumpukan, dan eksekusinya dilanjutkan.\n\nHere's the context stack when we entered the subcall `pow(2, 2)`:\nDisini tumpukan konteks ketika kita memasuki subcallnya `pow(2, 2)`;\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nKonteks eksekusi baru yang sekarang berada di atas (dan jelas), dan konteks yang sebelumnya berada dibawah.\n\nKetika kita menyelesaikan subcall -- itu akan mudah untuk melanjutkan konteks sebelumnya, karena itu tetap menyimpan kedua variabel dan tempat yang tepat dimana kode itu berhenti.\n\n```smart\nDisini dialam gambar kita gunakan kata \"line\", sebagai contoh disana terdapat satu subcall didalam baris, tapi secara umum sebuah baris dari kode mungkin mengandung subcall ganda, seperti `pow(…) + pow(…) + somethingElse(…)`.\n\nJadi itu harus menjadi lebih presisi untuk dikatakan eksekusi berlanjut \"langsung seterlah subcall\".\n```\n\n### pow(2, 1)\n\nProsesnya diulangi: subcall baru dibuat pada baris `5`, sekarang dengan argumen `x=2`, `n=1`.\n\nSebuah konteks eksekusi baru dibuat, yang sebelumnya didorong dari atas tumpukan:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 1, at line 1 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 1)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nDisana terdapat 2 konteks lama sekarang dan 1 sedang berjalan untuk `pow(2, 1)`.\n\n### Keluar\n\nSelama eksekusi dari `pow(2, 1)`, tidak seperti sebelumnya, kondisi `n == 1` sekarang bernilai true, jadi cabang pertama dari `if` bekerja:\n\n```js\nfunction pow(x, n) {\n  if (n == 1) {\n*!*\n    return x;\n*/!*\n  } else {\n    return x * pow(x, n - 1);\n  }\n}\n```\n\nDisana sekarang tidak ada lagi pemanggilan bercabang, jadi fungsinya selesai, mengembalikan `2`.\n\nSetelah fungsinya selesai, konteks eksekusinya tidak dibutuhkan lagi, jadi itu akan dihilangkan dari memori. Satu yang sebelumnya dikembalikan dari atas tumpukan:\n\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 2, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 2)</span>\n  </li>\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nEksekusi dari `pow(2, 2)` dilanjutkan. Itu telah mempunyai hasil dari subcall `pow(2, 1)`, jadi itu bisa menyelesaikan evaluasi dari `x * pow(x, n - 1)`, mengembalikan `4`.\n\nKonteks sebelumnya dikembalikan:\n\n<ul class=\"function-execution-context-list\">\n  <li>\n    <span class=\"function-execution-context\">Context: { x: 2, n: 3, at line 5 }</span>\n    <span class=\"function-execution-context-call\">pow(2, 3)</span>\n  </li>\n</ul>\n\nKetika itu selesai, kita mempunyai hasil dari `pow(2, 3) = 8`.\n\nDalam kasus ini kedalaman rekursinya adalah: **3**.\n\nSeperti yang bisa kita lihat dari ilustrasi diatas, kedalaman rekursi sama dengan nilai maksimal dari konteks didalam tumpukan.\n\nCatatan kebutuhan memori. Konteks memakan memori. Didalam kasus ini, menaikan dengan pangkat dari `n` sebenarnya membutuhkan memori sebanyak `n` konteks, untuk semua nilai terendah dari `n`.\n\nAlgoritma berbasis perulangan lebih menghemat memori:\n\n```js\nfunction pow(x, n) {\n  let result = 1;\n\n  for (let i = 0; i < n; i++) {\n    result *= x;\n  }\n\n  return result;\n}\n```\n\nInteratif `pow` menggunakan konteks tunggal mengganti `i` dan `result` didalam prosesnya. Kebutuhan memorinya kecil, tidak berubah-ubah dan tidak tergantung kepada `n`.\n\n**Rekursi apapun bisa ditulis ulang sebagai perulangan. Varian perulangan biasanya bisa dibuat lebih efektif.**\n\n...Tapi terkadang menulis ulang bukanlah hal yang sepele, terutama ketika fungsi menggunakan pemanggilan rekursif yang berbeda tergantung dari kondisi dan menyatukan hasil mereka atau cabangnya lebih rumit. Dan optimasinya mungkin tidak dibutuhkan dan benar-benar menghabiskan tenaga.\n\nRekursi bisa memberikan kode yang lebih pendek, lebih mudah dimengerti dan didukung. Optimasi tidak dibutuhkan di setiap tempat, kebanyakan kita butuh kode yang bagus, itulah kenapa itu digunakan.\n\n## Rekursif traversal\n\nPenerapan bagus lainnya dari rekursi adalah rekursif traversal.\n\nBayangkan, kita punya sebuah perusahaan. Struktur karyawannya bisa dipresentasikan sebagai sebuah objek:\n\n```js\nlet company = {\n  sales: [{\n    name: 'John',\n    salary: 1000\n  }, {\n    name: 'Alice',\n    salary: 1600\n  }],\n\n  development: {\n    sites: [{\n      name: 'Peter',\n      salary: 2000\n    }, {\n      name: 'Alex',\n      salary: 1800\n    }],\n\n    internals: [{\n      name: 'Jack',\n      salary: 1300\n    }]\n  }\n};\n```\n\nDengan kata lain, perusahaannya mempunyai departemen.\n\n- Sebuah departemen mungkin mempunyai sebuah array untuk staf. Contoh, departemen `sales` mempunyai dua karyawan John dan Alice.\n- Atau sebuah departemen mungkin dibagi menjadi sub-departemen, like `development` mempunyai dua cabang: `sites` dan `internals`. Untuk masin-masing memiliki stafnya masing-masing.\n- Hal yang mungkin terjadi adalah ketika sub-departemennya berkembang, itu akan terbagi menjadi sub-sub-departemen (tau tim).\n\n    Contoh, departemen `sites` di masa depan mungkin akan terbagi menjadi tim `siteA` dan `siteB`. Dan mereka, memiliki kemungkinan, terbagi lagi. Itu bukanlah sebuah gambaran, hanya sesuatu yang bisa terfikirkan.\n\nSekarang kita ingin sebuah fungsi untuk mendapatkan jumlah dari seluruh gaji. Bagaiman kita melakukannya?\n\nSebuah pendekatan iteratif tidaklah mudah, karena strukturnya tidak sederhana. Pertama mungkin untuk membuat perulangan `for`didalam `company` dengan sub-perulangan bercabang didalam departemen level 1. Tapi kita butuh lebih banyak sub-perulangan untuk mengiterasi staf didalam departemen level 2 seperti `sites`... Dan lalu sub-perulangan lainnya didalam departemen level 3 yang mungkin muncul di masa mendatang? Jika kita menggunakan 3-4 sub-perulangan didalam kode untuk menjelajahi objek tunggal, itu akan terlihat jelek.\n\nAyo kita coba rekursi.\n\nSeperti yang bisa kita lihat, ketika fungsi mendapatkan departemen untuk dijumlahkan, disana terdapat dua kemungkinan:\n\n1. Antara itu adalah sebuah departemen \"simpel\" dengan sebuah array dari orang -- lalu kita bisa menjumlahkan gajinya dengan perulangan yang sederhana.\n2. Atau itu adalah *sebuah objek* dengan `N` sub-departemen -- maka kita bisa membuat pemanggilan rekursif `N` untuk mendapatkan jumlah untuk setiap sub-departemen dan menjumlahkan hasilnya.\n\nDalam kasus pertama adalah dasar dari rekursi, kasus biasa, ketika kita mendapatkan sebuah array.\n\nKasus kedua ketika kita mendapatkan sebuah objek adalah langkah rekursif. Sebuah task yang kompleks dibagi menjadi sub-task untuk departemen yang lebih kecil. Mereka mungkin nanti akan terbagi lagi, tapi cepat atau lambat pembagiannya akan selesai pada (1).\n\nx\nAlgoritmanya mungkin lebih mudah untuk dibaca dari kodenya:\n\n\n```js run\nlet company = { // objek yang sama, dikompresi untuk keringkasan\n  sales: [{name: 'John', salary: 1000}, {name: 'Alice', salary: 1600 }],\n  development: {\n    sites: [{name: 'Peter', salary: 2000}, {name: 'Alex', salary: 1800 }],\n    internals: [{name: 'Jack', salary: 1300}]\n  }\n};\n\n// Fungsinya melakukan pekerjaannya\n*!*\nfunction sumSalaries(department) {\n  if (Array.isArray(department)) { // kasus (1)\n    return department.reduce((prev, current) => prev + current.salary, 0); // jumlahkan arraynya\n  } else { // kasus (2)\n    let sum = 0;\n    for (let subdep of Object.values(department)) {\n      sum += sumSalaries(subdep); // secara rekursif memanggil sub-departemen, jumlahkan hasilnya\n    }\n    return sum;\n  }\n}\n*/!*\n\nalert(sumSalaries(company)); // 7700\n```\n\nKodenya pendek dan mudah untuk dimengerti (semoga?). Itulah kemampuan dari rekursi. Itu juga bekerja untuk level apapun dari sub-departemen bercabang.\n\nIni adalah diagram dari pemanggilannya:\n\n![recursive salaries](recursive-salaries.svg)\n\nKita bisa dengan mudah melihat prinsipnya: untuk sebuah objek sub-pemanggilan `{...}` dibuat, semerata array `[...]` adalah daun dari pohon rekursi, mereka memberikan hasil secara langsung.\n\nIngat bahwa kodenya menggunakan fitur pintar yang sudah kita bahas sebelumnya:\n\n- Metode `arr.reduce` menjelaskan didalam bab <info:array-methods> untuk mendapatkan jumlah dari array.\n- Perulangan `for(val of Object.values(obj))` untuk mengiterasi nilai didalam objek: `Object.values` mengembalikan sebuah array darinya.\n\n\n## Struktur rekursif\n\nSebuah struktur data rekursif (ditetapkan secara rekursif) adalah sebuah struktur yang mengulangi dirinya-sendiri dalam beberapa bagian.\n\nKita juga telah melihatnya didalam contoh struktur perusahaan diatas.\n\nSebuah *departemen* perusahaan adalah:\n- Diantara sebuah array dari orang-orang\n- Atau sebuah objek dengan *departemen*.\n\nUntuk seorang pengembang-web disana terdapat contoh yang lebih baik: HTML dan dokumen XML.\n\nDidalam dokumen HTML, sebuah *tag-HTML* mungkin mengandung daftar dari:\n- Potongan-potongan text.\n- Komentar-komentar HTML.\n- *Tag-HTML* Lainnya (itu mungkin saja mengandung potongan text/komentar atau tag lainnya).\n\nItu sekali lagi adalah definisi rekursif.\n\nUntuk pemahaman lebih baik, kita akan memperlajari satu lagi struktur rekursif bernama \"Linked list\" itu mungkin sebuah alternatif yang bagus untuk array dalam beberapa kasus.\n\n### Linked list\n\nBayangkan, kita ingin menyimpan sebuah daftar terstruktur didalam sebuah objek.\n\nPilihan naturalnya mungkin sebuah array:\n\n```js\nlet arr = [obj1, obj2, obj3];\n```\n\n...Tapi disana terdapat sebuah masalah dengan array. Operasi \"delete element\" dan \"insert elemen\" sangatlah mahal. Contoh, operasi `arr.unshift(obj)` harus memberikan nomor baru untuk membuat ruang untuk `obj` baru, dan jika arraynya sangat besar, itu akan memakan waktu. Sama dengan `arr.shift()`.\n\nSatu-satunya modifikasi struktural yang tidak membutuhkan penomoran secara besar-besaran adalah itu yang beroperasi dengan akhiran dari array: `arr.push/pop`. Jadi sebuah array bisa menjadi cukup lambat untuk antrian yang panjang, ketika kita harus bekerja dengan awalannya.\n\nAlternatifnya, jika kita benar-benar membutuhkan memasukan/menghapus dengan cepat, kiat bisa memilih data struktur lainnya bernama [linked list](https://en.wikipedia.org/wiki/Linked_list).\n\n*Element linked list* didefinisikan secara rekursif sebagai sebuah objek dengan:\n- `value`.\n- `next` properti yang mereferensi *elemen linked list* selanjutnya atau `null` jika sudah mencapai akhir.\n\nContoh:\n\n```js\nlet list = {\n  value: 1,\n  next: {\n    value: 2,\n    next: {\n      value: 3,\n      next: {\n        value: 4,\n        next: null\n      }\n    }\n  }\n};\n```\n\nRepresentasi grafikal dari sebuah list:\n\n![linked list](linked-list.svg)\n\nAlternatif kode untuk pembuatan:\n\n```js no-beautify\nlet list = { value: 1 };\nlist.next = { value: 2 };\nlist.next.next = { value: 3 };\nlist.next.next.next = { value: 4 };\nlist.next.next.next.next = null;\n```\n\nDisini kita bisa melihat lebih jelas bahwa disana terdapat beberapa objek, masing-masing memiliki `value` dan `next` mengarah ke objek disisinya. Variabel `list` adalah objek pertama didalam rantainya, jadi pointer `next` selanjutnya dari itu bisa kita dapat dari elemen apapun.\n\nList-nya bisa dengan mudah dibagi menjadi beberapa bagian dan lalu disatukan kembali:\n\n```js\nlet secondList = list.next.next;\nlist.next.next = null;\n```\n\n![linked list split](linked-list-split.svg)\n\nUntuk menyatukan:\n\n```js\nlist.next.next = secondList;\n```\n\nDan tentu saja kita bisa memasukan atau menghapus item dari manapun.\n\nContoh, untuk memasukan nilai baru, kita harus memperbaharui awalan dari list-nya:\n\n```js\nlet list = { value: 1 };\nlist.next = { value: 2 };\nlist.next.next = { value: 3 };\nlist.next.next.next = { value: 4 };\n\n*!*\n// memasukan nilai baru kedalam list-nya\nlist = { value: \"new item\", next: list };\n*/!*\n```\n\n![linked list](linked-list-0.svg)\n\nUntuk menghapus nilai dari tengah, ganti `next` dengan yang sebelumnya:\n\n```js\nlist.next = list.next.next;\n```\n\n![linked list](linked-list-remove-1.svg)\n\nKita membuat `list.next` melompati `1` menuju nilai `2`. Nilai `1` sekarang tidak termasuk dari rentetannya. Jika itu tidak tersimpan dimanapun, itu akan secara otomatis dihapus dari memori.\n\nTidak seperti array, disana tidak terdapat penomoran urang secara besar-besaran, kita bisa dengan mudah menyusun kembali elemen-elemennya.\n\nUmumnya, list tidak selalu lebih baik daripada array. Sebaliknya semua orang harusnya hanya menggunakan list.\n\nKekurangannya adalah kita tidak bisa dengan mudah mengakses sebuah elemen dengan nomornya. Didalam sebuah array kita bisa dengan mudah: `arr[n]` adalah sebuah referensi langsung. Tapi didalam list kita harus memulai dari item pertama dan maju `next` `N` kali untuk mendapatkan elemen ke Nth.\n\n...Tapi kita tidak selalu butuh operasi seperti itu. Contoh, ketika kita membutuhkan sebuah antrian atau bahkan [deque](https://en.wikipedia.org/wiki/Double-ended_queue) -- struktur urutannya harus mengijinkan penambahan/penghapusan elemen dengan cepat dari kedua sisi tapi akses kedalam bagian tengah tidak dibutuhkan.\n\nList bisa ditingkatkan:\n- Kita bisa menambahkan properti `prev` didalam penambahan untuk `next` untuk mereferensikan elemen sebelumnya, untuk berpindah mundur dengan mudah.\n- Kita juga bisa menambahkan sebuah variabel bernama `tail` mereferensi elemen terakhir dari list (dan memperbaharuinya ketika menambahkan/menghapus elemen dari ujung terakhir).\n- Struktur data mungkin bervariasi tergantung dari kebutuhan kita.\n\n## Rangkuman\n\nIstilah:\n- *Rekursi* adalah sebuah istilah programming yang berarti memanggil fungsi dari dirinya sendiri. Fungsi rekursi bisa digunakan untuk memecahkan tugas dengan cara yang elegan.\n\n    Ketika sebuah fungsi memanggil dirinya sendiri, itulah yang disebut dengan *langkah rekursi*. *Dasar* dari rekursi adalah sebuah argumen fungsi yang membuat task menjadi lebih sederhana yang dimana fungsinya tidak membuat pemanggilan lebih jauh.\n\n- Sebuah struktur data [didefinisikan secara rekursif](https://en.wikipedia.org/wiki/Recursive_data_type) adalah struktur data yang bisa mendefinisikan menggunakan dirinya sendiri.\n\n    Contoh, linked list bisa didefinisikan sebagai sebuah struktur data yang terdiri dari sebuah objek yang mereferensi sebuah list (atau null).\n\n    ```js\n    list = { value, next -> list }\n    ```\n\n    Pohon seperti pohon elemen HTML atau pohon departemen dari bab ini juga secara natural rekursif: cabang mereka dan setuap cabang mempunyai cabang lainnya.\n\n    Fungsi rekursif bisa digunakan untuk menyusurinya seperti yang telah kita lihat didalam contoh `sumSalary`.\n\nFungsi rekursif manapun bisa ditulis ulang menggunakan iterasi. Dan itu terkadang membutuhkan hal-hal optimasi. Tapi untuk kebanyakan task sebuah solusi rekursif cukup cepat dan mudah untuk ditulis dan didukung."
  },
  {
    "path": "1-js/06-advanced-functions/01-recursion/head.html",
    "content": "<style>\n.function-execution-context-list {\n  margin: 0;\n  padding: 0;\n  overflow: auto;\n}\n\n.function-execution-context {\n  border: 1px solid black;\n  font-family: 'DejaVu Sans Mono', 'Lucida Console', 'Menlo', 'Monaco', monospace;\n  padding: 4px 6px;\n  margin: 0 4px;\n}\n\n.function-execution-context-call {\n  color: gray;\n}\n\n.function-execution-context-call::before {\n  content: ' call: ';\n}\n\n.function-execution-context-list li:first-child {\n  font-weight: bold;\n}\n</style>\n"
  },
  {
    "path": "1-js/06-advanced-functions/02-rest-parameters-spread/article.md",
    "content": "# Parameter rest dan sintaks spread\n\nBanyak fungsi bawaan Javascript yang mendukung argumen dengan angka yang panjang.\n\nContoh:\n\n- `Math.max(arg1, arg2, ..., argN)` -- mengembalikan nilai terbesar dari argumen.\n- `Object.assign(dest, src1, ..., srcN)` -- menyalin properti dari `src1..N` kedalam `dest`.\n- ...dan lainnya.\n\nDidalam bab ini kita akan belajar bagaimana cara untuk melakukan hal yang sama, bagaimana cara untuk mengirim array kepada fungsi seperti itu sebagai parameter.\n\n## Parameter rest `...`\n\nSebuah fungsi dapat dipanggil dengan jumlah argumen berapapun, tidak peduli bagaimana itu didefinisikan.\n\nSeperti ini:\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nalert( sum(1, 2, 3, 4, 5) );\n```\n\nDisana tidak akan terdapat error karena argumen \"berlebihan\". Tapi tentu saja hasilnya hanya dua angka pertama yang dihitung.\n\nSisa parameternya bisa digunakan didalam fungsi dengan menggunakan tiga titik `...` diikuti nama dari array yang akan berisi mereka. Titik secara harfiah berarti \"kumpulkan sisa parameter didalam array\".\n\nContoh, untuk mengumpulkan seluruh argumen menjadi array `args`:\n\n```js run\nfunction sumAll(...args) { // args adalah nama dari arraynya\n  let sum = 0;\n\n  for (let arg of args) sum += arg;\n\n  return sum;\n}\n\nalert( sumAll(1) ); // 1\nalert( sumAll(1, 2) ); // 3\nalert( sumAll(1, 2, 3) ); // 6\n```\n\nKita bisa memilih untuk mendapatkan parameter pertama sebagai variabel, dan sisanya dikumpulkan.\n\nDisini dua parameter pertama akan dimasukan kedalam variabel dan sisanya akan masuk kedalam array `titles`:\n\n```js run\nfunction showName(firstName, lastName, ...titles) {\n  alert( firstName + ' ' + lastName ); // Julius Caesar\n\n  // sisanya masuk kedalam array titles\n  // i.e. titles = [\"Consul\", \"Imperator\"]\n  alert( titles[0] ); // Consul\n  alert( titles[1] ); // Imperator\n  alert( titles.length ); // 2\n}\n\nshowName(\"Julius\", \"Caesar\", \"Consul\", \"Imperator\");\n```\n\n````warn header=\"Parameter rest harus berada di akhir\"\nParameter rest mengumpulkan seluruh sisa argumen, jadi contoh dibawah tidak dapat dimengerti dan akan menyebabkan error:\n\n```js\nfunction f(arg1, ...rest, arg2) { // arg2 after ...rest ?!\n  // error\n}\n```\n\n`...rest` harus selalu berada di akhir.\n````\n\n## Variable \"arguments\"\n\nJuga terdapat objek spesial seperti array bernama `arguments` yang mengandung seluruh argumen dengan indeksnya.\n\nContoh:\n\n```js run\nfunction showName() {\n  alert( arguments.length );\n  alert( arguments[0] );\n  alert( arguments[1] );\n\n  // dapat diiterasi\n  // for(let arg of arguments) alert(arg);\n}\n\n// menampilkan: 2, Julius, Caesar\nshowName(\"Julius\", \"Caesar\");\n\n// menampilkan: 1, Ilya, undefined (tidak ada argumen kedua)\nshowName(\"Ilya\");\n```\n\nDahulu, parameter rest tidak ada didalam bahasa pemrograman, dan menggunakan `arguments` hanyalah satu-satunya cara untuk mendapatkan seluruh argumen dari sebuah fungsi. Dan itu tetap bekerja, kita bisa menemukannya di kode-kode jadul.\n\nTapi kekurangannya adalah walaupun `arguments` seperti array dan bisa diiterasi, itu bukanlah sebuah array. Itu tidak mendukung metode-metode array, jadi kita tidak bisa memanggil untuk contoh `arguments.map(...)`.\n\nJuga, itu selalu mengandung seluruh argumen. Kita tidak bisa menangkapnya dalam beberapa bagian, seperti yang kita lakukan dengan parameter rest.\n\nJadi ketika kita membutuhkan fiturnya, parameter rest lebih disukai.\n\n````smart header=\"Arrow function tidak memiliki `\\\"arguments\\\"`\"\nJika kita ingin mengakses objek `arguments`dari sebuah fungsi panah/arrow function, itu akan mengambilnya dari fungsi \"normal\" terluar.\n\nContoh:\n\n```js run\nfunction f() {\n  let showArg = () => alert(arguments[0]);\n  showArg();\n}\n\nf(1); // 1\n```\n\nSeperti yang kita ingat, arrow function tidak memiliki `this` mereka sendiri. Sekarang kita tahu mereka tidak memiliki objek `arguments` yang spesial juga.\n````\n\n\n## Sintaks spread [#spread-syntax]\n\nKita baru saja melihat bagaimana cara untuk mendapatkan sebuah array dari daftar dari sebuah parameter-parameter.\n\nTapi terkadang kita perlu untuk melakukan hal yang sama dengan terbalik.\n\nContoh, terdapat fungsi bawaan [Math.max](mdn:js/Math/max) yang mengembalikan angka terbesar dari list:\n\n```js run\nalert( Math.max(3, 5, 1) ); // 5\n```\n\nSekarang kita bayangkan kita mempunyai sebuah array `[3, 5, 1]. Bagaimana caranya kita memanggil `Math.max` dengan itu?\n\nBerikan itu \"kedalamnya\" tidak akan bekerja, karena `Math.max` mengharapkan sebuah daftar dari argumen numerik, bukan dari array tunggal:\n\n```js run\nlet arr = [3, 5, 1];\n\n*!*\nalert( Math.max(arr) ); // NaN\n*/!*\n```\n\nDan tentu saja kita tidak bisa secara manual memasukan itemnya kedalam kode `Math.max(arr[0], arr[1], arr[2])`, karena kita mungkin tidak yakin ada berapa elemen didalamnya. Lalu saat skripnya dieksekusi, disana mungkin terdapat banyak, atau mungkin tidak ada. Dan itu bukanlah hal yang bagus.\n\n*Sintaks spread* datang untuk membantu! Itu terlihat sama dengan parameter rest, juga menggunakan `...`, tapi itu melakukan yang sebaliknya.\n\nKetika `...arr` digunakan didalam pemanggilan fungsi, itu \"memperluas\" sebuah objek yang bisa diiterasi `arr` kedalam daftar dari argumen.\n\nUntuk `Math.max`:\n\n```js run\nlet arr = [3, 5, 1];\n\nalert( Math.max(...arr) ); // 5 (spread mengubah array menjadi daftar dari argumen)\n```\n\nKita juga bisa memberikan beberapa hal yang bisa diiterasi dengan cara ini:\n\n```js run\nlet arr1 = [1, -2, 3, 4];\nlet arr2 = [8, 3, -8, 1];\n\nalert( Math.max(...arr1, ...arr2) ); // 8\n```\n\nKita juga bisa mengkombinasikan sintaks spread dengan nilai normal:\n\n\n```js run\nlet arr1 = [1, -2, 3, 4];\nlet arr2 = [8, 3, -8, 1];\n\nalert( Math.max(1, ...arr1, 2, ...arr2, 25) ); // 25\n```\n\nJuga, sintaks spread bisa digunakan untuk menyatukan array-array:\n\n```js run\nlet arr = [3, 5, 1];\nlet arr2 = [8, 9, 15];\n\n*!*\nlet merged = [0, ...arr, 2, ...arr2];\n*/!*\n\nalert(merged); // 0,3,5,1,2,8,9,15 (0, lalu arr, lalu 2, lalu arr2)\n```\n\nDalam contoh diatas kita menggunakan sebuah array untuk mendemonstrasikan sintaks spread, tapi hal yang bisa diiterasi apapun bisa digunakan.\n\nContoh, disini kita menggunakan sintaks spread untuk mengubah string menjadi array dari karakter-karakter:\n\n```js run\nlet str = \"Hello\";\n\nalert( [...str] ); // H,e,l,l,o\n```\n\nSintaks spread secara internal menggunakan iterator untuk menggabungkan elemen-elemen, cara yang sama seperti yang dilakukan `for..of`.\n\nJadi, untuk sebuah string, `for..of` mengembalikan karakter-karakter dan `...str` menjadi `\"H\",\"e\",\"l\",\"l\",\"o\"`. Daftar dari karakter-karakter diberikan kepada penginisialisasi array `[...str]`.\n\nUntuk task tertentu kita bisa juga menggunakan `Array.from`, karena itu akan mengkonversi sebuah hal yang bisa diiterasi (seperti string) menjadi sebuah array:\n\n```js run\nlet str = \"Hello\";\n\n// Array.from mengubah sebuah iterabel menjadi sebuah array\nalert( Array.from(str) ); // H,e,l,l,o\n```\n\nHasilnya akan sama seperti `[...str]`.\n\nTapi disana terdapat perbedaan yang tipis diantara `Array.from(obj)` dan `[...obj]`:\n\n- `Array.from` dapat dioperasikan di \"hal yang seperti array\" dan \"hal yang bisa diiterasi\".\n- Sintaks spread hanya bekerja dengan hal yang bisa diiterasi.\n\nJadi, task untuk mengubah sesuatu menjadi sebuah array, `Array.from` cenderung lebih banyak digunakan.\n\n\n## Mendapatkan salinan baru dari sebuah array/objek\n\nInget ketika kita berbicara tentang `Object.assign()` [sebelumnya](info:object-copy#cloning-and-merging-object-assign)?\n\nItu adalah hal yang bisa dilakukan untuk melakukan hal yang sama dengan sintaks spread.\n\n```js run\nlet arr = [1, 2, 3];\nlet arrCopy = [...arr]; // sebarkan arraynya menjadi list dari parameter\n                        // lalu masukan hasilnya kedalam array yang baru\n\n// apakah arraynya mempunyai konten yang sama?\nalert(JSON.stringify(arr) === JSON.stringify(arrCopy)); // true\n\n// apakah arraynya sama?\nalert(arr === arrCopy); // false (bukanlah referensi yang sama)\n\n// memodifikasi array awal kita tidak memodifikasi salinannya:\narr.push(4);\nalert(arr); // 1, 2, 3, 4\nalert(arrCopy); // 1, 2, 3\n```\n\nPerhatikan bahwa melakukan hal yang sama seperti menyalin sebuah objek adalah hal yang bisa dilakukan:\n\n```js run\nlet obj = { a: 1, b: 2, c: 3 };\nlet objCopy = { ...obj }; // sebarkan objeknya menjadi daftar dari parameter\n                          // lalu kembalikan hasilnya kedalam objek baru.\n\n// apakah objeknya memiliki kontent yang sama?\nalert(JSON.stringify(obj) === JSON.stringify(objCopy)); // true\n\n// apakah objeknya sama?\nalert(obj === objCopy); // false (not same reference)\n\n// memodifikasi objel awal kita tidak berarti memodifikasi salinannya:\nobj.d = 4;\nalert(JSON.stringify(obj)); // {\"a\":1,\"b\":2,\"c\":3,\"d\":4}\nalert(JSON.stringify(objCopy)); // {\"a\":1,\"b\":2,\"c\":3}\n```\n\nMenyalin objek dengan cara ini lebih pendek daripada `let objCopy = Object.assign({}, obj);` atau untuk sebuah array `let arrCopy = Objek.assign([], arr);` jadi kita lebih memilih menggunakannya kapanpun bila bisa digunakan.\n\n\n## Ringkasan\n\nketika kita melihat `\"...\"` didalam kode, itu adalah parameter rest atau sintaks spread.\n\nTerdapat sebuah cara yang mudah untuk membedakan mereka:\n\n- Ketika `...` berada di akhiran dari parameter fungsi, itu adalah \"parameter rest\" dan menggabungkan sisa dari daftar argumen menjadi sebuah array.\n- Ketika `...` muncul didalam pemanggilan fungsi atau sejenisnya, itu dipanggil dengan \"sintaks spread\" dan membentangkan array menjadi sebuah list.\n\nPenggunaan pola:\n\n- Paramter rest digunakan untuk membuat fungsi yang dapat menerima argumen dengan jumlah berapapun.\n- Sintkas spread digunakan untuk mengirimkan sebuah array kedalam sebuah fungsi yang biasanya membutuhkan daftar dari beberapa argumen.\n\nBersama mereka membantu menggunakan sebuah list dan sebuah array dari parameter dengan mudah.\n\nSemua argumen dari sebuah pemanggilan fungsi juga tersedia di argumen dengan \"gaya-lama\": objek seperti array yang bisa diiterasi.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/1-closure-latest-changes/solution.md",
    "content": "Jawabannya adalah: **Pete**.\n\nSebuah fungsi mendapatkan variabel yang sekarang, nilai paling terbaru akan digunakan.\n\nNilai variabel lama tidak akan tersimpan dimanapun. Ketika sebuah fungsi menginginkan sebuah variabel, nilai terbaru akan diambil dari lingkungan leksikalnya atau dari luar.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/1-closure-latest-changes/task.md",
    "content": "Nilai penting: 5\n\n---\n\n# Apakah sebuah fungsi akan mengambil perubahan terakhir?\n\nFungsi sayHi menggunakan nama variabel dari luar. Ketika fungsinya berjalan, nilai manakah yang akan digunakan?\n\n```js\nlet name = \"John\";\n\nfunction sayHi() {\n  alert(\"Hi, \" + name);\n}\n\nname = \"Pete\";\n\nsayHi(); // apakah yang akan tampil: \"John\" atau \"Pete\"?\n```\n\nSituasi seperti itu adalah hal yang biasa didalam peramban dan pengembangan di bagian server. Sebuah fungsi mungkin sudah dijadwalkan untuk dieksekusi nanti daripada saat dibuat, untuk contoh setelah sebuah aksi user atau setelah me-request ke jaringan.\n\nJadi, pertanyaannya adalah: apakah nilai terakhir akan diambil?\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/solution.js",
    "content": "function makeArmy() {\n\n  let shooters = [];\n\n  for(let i = 0; i < 10; i++) {\n    let shooter = function() { // fungsi shooter\n      alert( i ); // harus menampilkan angkanya\n    };\n    shooters.push(shooter);\n  }\n\n  return shooters;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/source.js",
    "content": "function makeArmy() {\n  let shooters = [];\n\n  let i = 0;\n  while (i < 10) {\n    let shooter = function() { // fungsi shooter\n      alert( i ); // harus menampilkan angkanya\n    };\n    shooters.push(shooter);\n    i++;\n  }\n\n  return shooters;\n}\n\n/*\nlet army = makeArmy();\n\narmy[0](); // angka shooter 0 menampilkan 10\narmy[5](); // dan angka 5 juga mengeluarkan 10...\n// ... seluruh shooter menampilkan 10 daripada nilai 0, 1, 2, 3...\n*/\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/_js.view/test.js",
    "content": "describe(\"army\", function() {\n\n  let army;\n  \n  before(function() {\n    army = makeArmy();\n    window.alert = sinon.stub(window, \"alert\");\n  });\n\n  it(\"army[0] shows 0\", function() {\n    army[0]();\n    assert(alert.calledWith(0));\n  });\n\n\n  it(\"army[5] shows 5\", function() {\n    army[5]();\n    assert(alert.calledWith(5));\n  });\n\n  after(function() {\n    window.alert.restore();\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/solution.md",
    "content": "\nLet's examine what exactly happens inside `makeArmy`, and the solution will become obvious.\n\n1. It creates an empty array `shooters`:\n\n    ```js\n    let shooters = [];\n    ```\n2. Fills it with functions via `shooters.push(function)` in the loop.\n\n    Every element is a function, so the resulting array looks like this:\n\n    ```js no-beautify\n    shooters = [\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); },\n      function () { alert(i); }\n    ];\n    ```\n\n3. The array is returned from the function.\n    \n    Then, later, the call to any member, e.g. `army[5]()` will get the element `army[5]` from the array (which is a function) and calls it.\n    \n    Now why do all such functions show the same value, `10`?\n    \n    That's because there's no local variable `i` inside `shooter` functions. When such a function is called, it takes `i` from its outer lexical environment.\n    \n    Then, what will be the value of `i`?\n    \n    If we look at the source:\n    \n    ```js\n    function makeArmy() {\n      ...\n      let i = 0;\n      while (i < 10) {\n        let shooter = function() { // shooter function\n          alert( i ); // should show its number\n        };\n        shooters.push(shooter); // add function to the array\n        i++;\n      }\n      ...\n    }\n    ```\n    \n    We can see that all `shooter` functions are created in the lexical environment of `makeArmy()` function. But when `army[5]()` is called, `makeArmy` has already finished its job, and the final value of `i` is `10` (`while` stops at `i=10`).\n    \n    As the result, all `shooter` functions get the same value from the outer lexical environment and that is, the last value, `i=10`.\n    \n    ![](lexenv-makearmy-empty.svg)\n    \n    As you can see above, on each iteration of a `while {...}` block, a new lexical environment is created. So, to fix this, we can copy the value of `i` into a variable within the `while {...}` block, like this:\n    \n    ```js run\n    function makeArmy() {\n      let shooters = [];\n    \n      let i = 0;\n      while (i < 10) {\n        *!*\n          let j = i;\n        */!*\n          let shooter = function() { // shooter function\n            alert( *!*j*/!* ); // should show its number\n          };\n        shooters.push(shooter);\n        i++;\n      }\n    \n      return shooters;\n    }\n    \n    let army = makeArmy();\n    \n    // Now the code works correctly\n    army[0](); // 0\n    army[5](); // 5\n    ```\n    \n    Here `let j = i` declares an \"iteration-local\" variable `j` and copies `i` into it. Primitives are copied \"by value\", so we actually get an independent copy of `i`, belonging to the current loop iteration.\n    \n    The shooters work correctly, because the value of `i` now lives a little bit closer. Not in `makeArmy()` Lexical Environment, but in the Lexical Environment that corresponds to the current loop iteration:\n    \n    ![](lexenv-makearmy-while-fixed.svg)\n    \n    Such a problem could also be avoided if we used `for` in the beginning, like this:\n    \n    ```js run demo\n    function makeArmy() {\n    \n      let shooters = [];\n    \n    *!*\n      for(let i = 0; i < 10; i++) {\n    */!*\n        let shooter = function() { // shooter function\n          alert( i ); // should show its number\n        };\n        shooters.push(shooter);\n      }\n    \n      return shooters;\n    }\n    \n    let army = makeArmy();\n    \n    army[0](); // 0\n    army[5](); // 5\n    ```\n    \n    That's essentially the same, because `for` on each iteration generates a new lexical environment, with its own variable `i`. So `shooter` generated in every iteration references its own `i`, from that very iteration.\n    \n    ![](lexenv-makearmy-for-fixed.svg)\n\nNow, as you've put so much effort into reading this, and the final recipe is so simple - just use `for`, you may wonder -- was it worth that?\n\nWell, if you could easily answer the question, you wouldn't read the solution. So, hopefully this task must have helped you to understand things a bit better. \n\nBesides, there are indeed cases when one prefers `while` to `for`, and other scenarios, where such problems are real.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/10-make-army/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Pasukan-pasukan fungsi\n\nKode berikut membuat array dari `shooters`.\n\nSetiap fungsi diinginkan untuk mengeluarkan angkanya sendiri. Tetapi ada yang salah...\n\n```js run\nfunction makeArmy() {\n  let shooters = [];\n\n  let i = 0;\n  while (i < 10) {\n    let shooter = function() { // fungsi shooter\n      alert( i ); // seharusnya mengeluarkan angkanya sendiri\n    };\n    shooters.push(shooter); // and add it to the array\n    i++;\n  }\n\n  // ...and return the array of shooters\n  return shooters;\n}\n\nlet army = makeArmy();\n\n*!*\n// semua penembak menunjukkan 10 bukannya angka mereka 0, 1, 2, 3...\narmy[0](); // 10 dari nomor penembak 0\narmy[1](); // 10 dari penembak nomor 1\narmy[2](); // 10 ...dan seterusnya.\n*/!*\n```\n\nMengapa semua penembak menunjukkan nilai yang sama?\n\nPerbaiki kode agar berfungsi sebagaimana mestinya.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/2-closure-variable-access/solution.md",
    "content": "Jawabannya adalah: **Pete**.\n\nFungsi `work()` didalam kode mendapatkan `name` dari tempat dimana itu dibuat daripada mereferensi dari luar lingkungannya :\n\n![](lexenv-nested-work.svg)\n\njadi, hasilnya adalah `\"Pete\"` disini.\n\nTapi jika disana tidak ada `let name` didalam `makeWorker()`, maka pencarian akan berlanjut ke luar dan mengambil variabel global seperti yang bisa kita lihat diatas. Di kasus ini hasilnya akan menjadi `\"John\"`."
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/2-closure-variable-access/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Variabel manakah yang tersedia?\n\nFungsi `makeWorker` dibawah membuat fungsi lainnya dan mengembalikannya. Fungsi baru itu bisa dipanggil dari manapun.\n\nAkankah itu mempunyai akses ke variabel luar dari tempat pembuatannya, atau dari tempat pemanggilannya, atau keduanya?\n\n```js\nfunction makeWorker() {\n  let name = \"Pete\";\n\n  return function() {\n    alert(name);\n  };\n}\n\nlet name = \"John\";\n\n// pembuatan fungsi\nlet work = makeWorker();\n\n// dipanggil\nwork(); // apakah yang akan tampil?\n```\n\nNilai manakah yang akan muncul? \"Pete\" atau \"John\"?\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/3-counter-independent/solution.md",
    "content": "Jawaban: **0,1.**\n\nFungsi `counter` dan `counter2` dibuat dengan panggilan fungsi `makeCounter` yang berbeda.\n\nJadi mereka memiliki lingkungan leksikal yang berbeda, dengan `count` mereka masing-masing.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/3-counter-independent/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Apakah para counter independen?\n\nDi sini kita membuat dua counter: `counter` dan `counter2` menggunakan fungsi `makeCounter` yang sama.\n\nApakah mereka independen? Apa yang akan counter kedua munculkan? `0,1` atau `2,3` atau yang lainnya?\n\n```js\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\nlet counter2 = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\n\n*!*\nalert( counter2() ); // ?\nalert( counter2() ); // ?\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/4-counter-object-independent/solution.md",
    "content": "\nTentu saja hal tersebut akan bekerja.\n\nKedua fungsi bersarang dibuat dengan lingkungan leksikal yang sama, jadi mereka membagi akses ke variabel `count` yang sama:\n\n```js run\nfunction Counter() {\n  let count = 0;\n\n  this.up = function() {\n    return ++count;\n  };\n  \n  this.down = function() {\n    return --count;\n  };\n}\n\nlet counter = new Counter();\n\nalert( counter.up() ); // 1\nalert( counter.up() ); // 2\nalert( counter.down() ); // 1\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/4-counter-object-independent/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Objek counter\n\nDisini kita memiliki objek counter yang dibuat dengan bantuan fungsi konstruktor.\n\nApakah hal tersebut akan bekerja? Apa yang akan muncul?\n\n```js\nfunction Counter() {\n  let count = 0;\n\n  this.up = function() {\n    return ++count;\n  };\n  this.down = function() {\n    return --count;\n  };\n}\n\nlet counter = new Counter();\n\nalert( counter.up() ); // ?\nalert( counter.up() ); // ?\nalert( counter.down() ); // ?\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/5-function-in-if/solution.md",
    "content": "Hasilnya yaitu **sebuah error**.\n\nFungsi `sayHi` dideklarasikan di dalam `if`, jadi fungsi tersebut hanya hidup di dalamnya. Tidak ada fungsi `sayHi` di luar."
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/5-function-in-if/task.md",
    "content": "\n# Fungsi di dalam if\n\nLihatlah kode di bawah ini. Apa hasil dari panggilan fungsi di baris terakhir?\n\n```js run\nlet phrase = \"Hello\";\n\nif (true) {\n  let user = \"John\";\n\n  function sayHi() {\n    alert(`${phrase}, ${user}`);\n  }\n}\n\n*!*\nsayHi();\n*/!*\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/6-closure-sum/solution.md",
    "content": "Agar kurung kedua berhasil, yang pertama harus mengembalikan sebuah fungsi.\n\nSeperti ini:\n\n```js run\nfunction sum(a) {\n\n  return function(b) {\n    return a + b; // mengambil \"a\" dari lingkungan leksikal luar\n  };\n\n}\n\nalert( sum(1)(2) ); // 3\nalert( sum(5)(-1) ); // 4\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/6-closure-sum/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Penjumlahan dengan closure\n\nBuatlah sebuah fungsi `sum` yang bekerja seperti ini: `sum(a)(b) = a+b`.\n\nYa, seperti ini, dengan kurung ganda (bukan salah ketik).\n\nSebagai contoh:\n\n```js\nsum(1)(2) = 3\nsum(5)(-1) = 4\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/7-let-scope/solution.md",
    "content": "Hasilnya adalah: **error**.\n\nCobalah jalankan ini:\n\n```js run\nlet x = 1;\n\nfunction func() {\n*!*\n  console.log(x); // ReferenceError: Cannot access 'x' before initialization\n*/!*\n  let x = 2;\n}\n\nfunc();\n```\n\nDidalam contoh ini kita bisa mengamati perbedaan aneh diantara variabel yang \"tidak-ada\" dan \"belum diinisialisasi\".\n\nMungkin seperti yang telah kamu baca didalam artikel [](info:closure), sebuah variabel dimulai didalam state \"belum diinisialisasi\" sejak saat eksekusinya memasuki blok kode (atau sebuah fungsi). Dan itu akan tetap belum diinisialisasi sampai statemen `let` yang bersangkutan.\n\nDengan kata lain, sebuah variabel secara teknis ada, tapi kita belum bisa menggunakannya sebelum `let`.\n\nKode diatas mendemonstrasikan hal itu.\n\n```js\nfunction func() {\n*!*\n  // variabel lokal x dikenal mesinnya di awal dari fungsinya,\n  // tapi \"belum diinisialisasi\" (tidak dapat digunakan) sampai let (\"zona mati\")\n  // karenanya terdapat error\n*/!*\n\n  console.log(x); // ReferenceError: Cannot access 'x' before initialization\n\n  let x = 2;\n}\n```\n\nZona tidak terpakai dari sebuah variabel ini (dari awal blok kode sampai `let`) terkadang dipanggil dengan \"zona mati\".\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/7-let-scope/task.md",
    "content": "nilai penting: 4\n\n---\n\n# Apakah variabelnya terlihat?\n\nApakah hasil dari kode ini?\n\n```js\nlet x = 1;\n\nfunction func() {\n  console.log(x); // ?\n\n  let x = 2;\n}\n\nfunc();\n```\n\nP.S. There's a pitfall in this task. The solution is not obvious.\nCatatan. Terdapat jebakan pada task ini. Solusinya menjadi kurang jelas.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/solution.js",
    "content": "\nfunction inArray(arr) {\n  return x => arr.includes(x);\n}\n\nfunction inBetween(a, b) {\n  return x => (x >= a && x <= b);\n}"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/source.js",
    "content": "\nlet arr = [1, 2, 3, 4, 5, 6, 7];\n\nfunction inBetween(a, b) {\n  // ...your code...\n}\n\nfunction inArray(arr) {\n  // ...your code...\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/_js.view/test.js",
    "content": "\ndescribe(\"inArray\", function() {\n  let arr = [1, 2, 3, 4, 5, 6, 7];\n\n  it(\"returns the filter for values in array\", function() {\n\n    let filter = inArray(arr);\n    assert.isTrue(filter(5));\n    assert.isFalse(filter(0));\n  });\n});\n\n\ndescribe(\"inBetween\", function() {\n\n  it(\"returns the filter for values between\", function() {\n    let filter = inBetween(3, 6);\n    assert.isTrue(filter(5));\n    assert.isFalse(filter(0));\n  });\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/solution.md",
    "content": "\n# Filter inBetween\n\n```js run\nfunction inBetween(a, b) {\n  return function(x) {\n    return x >= a && x <= b;\n  };\n}\n\nlet arr = [1, 2, 3, 4, 5, 6, 7];\nalert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6\n```\n\n# Filter inArray\n\n```js run demo\nfunction inArray(arr) {\n  return function(x) {\n    return arr.includes(x);\n  };\n}\n\nlet arr = [1, 2, 3, 4, 5, 6, 7];\nalert( arr.filter(inArray([1, 2, 10])) ); // 1,2\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/8-filter-through-function/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Filter dengan fungsi\n\nKita memiliki method bawaan `arr.filter(f)` untuk array. Method tersebut menyaring seluruh elemen menggunakan fungsi `f`. Apabila mengembalikan `true`, maka elemen tersebut dikembalikan di array hasil.\n\nBuatlah filter \"yang siap pakai\":\n\n- `inBetween(a, b)` -- antara `a` dan `b` atau sama dengan (inklusif).\n- `inArray([...])` -- terkandung di dalam array.\n\nPenggunaannya harus seperti ini:\n\n- `arr.filter(inBetween(3,6))` -- menyimpan hanya nilai di antara 3 dan 6.\n- `arr.filter(inArray([1,2,3]))` -- menyimpan elemen apabila sama dengan salah satu dari `[1,2,3]`.\n\nSebagai contoh:\n\n```js\n/* .. implementasi inBetween dan inArray */\nlet arr = [1, 2, 3, 4, 5, 6, 7];\n\nalert( arr.filter(inBetween(3, 6)) ); // 3,4,5,6\n\nalert( arr.filter(inArray([1, 2, 10])) ); // 1,2\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js",
    "content": "function byField(fieldName){\n  return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js",
    "content": "function byField(fieldName){\n\n  // Your code goes here.\n\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js",
    "content": "describe(\"byField\", function(){\n\n  let users = [\n    { name: \"John\", age: 20, surname: \"Johnson\" },\n    { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n  ];\n\n  it(\"sorts users by name\", function(){\n    let nameSortedKey = [\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    ];\n    let nameSortedAnswer = users.sort(byField(\"name\"));\n    assert.deepEqual(nameSortedKey, nameSortedAnswer);\n  });\n\n  it(\"sorts users by age\", function(){\n    let ageSortedKey = [\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n    ];\n    let ageSortedAnswer = users.sort(byField(\"age\"));\n    assert.deepEqual(ageSortedKey, ageSortedKey);\n  });\n\n  it(\"sorts users by surname\", function(){\n    let surnameSortedKey = [\n      { name: \"Ann\", age: 19, surname: \"Hathaway\" },\n      { name: \"John\", age: 20, surname: \"Johnson\"},\n      { name: \"Pete\", age: 18, surname: \"Peterson\" },\n    ];\n    let surnameSortedAnswer = users.sort(byField(\"surname\"));\n    assert.deepEqual(surnameSortedAnswer, surnameSortedKey);\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md",
    "content": "\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/9-sort-by-field/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Urutkan berdasarkan field\n\nKita memiliki array objek untuk diurutkan:\n\n```js\nlet users = [\n  { name: \"John\", age: 20, surname: \"Johnson\" },\n  { name: \"Pete\", age: 18, surname: \"Peterson\" },\n  { name: \"Ann\", age: 19, surname: \"Hathaway\" }\n];\n```\n\nCara yang biasa dilakukan yaitu:\n\n```js\n// berdasarkan name (Ann, John, Pete)\nusers.sort((a, b) => a.name > b.name ? 1 : -1);\n\n// berdasarkan age (Pete, Ann, John)\nusers.sort((a, b) => a.age > b.age ? 1 : -1);\n```\n\nApakah kita dapat membuatnya lebih ringkas, seperti ini?\n\n```js\nusers.sort(byField('name'));\nusers.sort(byField('age'));\n```\n\nJadi, daripada menulis sebuah fungsi, cukup tulis `byField(fieldName)`.\n\nTulislah fungsi `byField` yang dapat digunakan untuk itu.\n"
  },
  {
    "path": "1-js/06-advanced-functions/03-closure/article.md",
    "content": "# Lingkup variabel, closure\n\nJavascript adalah bahasa yang berorientasi-fungsi. Itu memberikan kita banyak kebebasan. Sebuah fungsi bisa dibuat kapanpun, diberikan sebagai argumen kedalam fungsi lain, dan lalu dipanggil dari kode yang benar-benar berbeda nanti.\n\nKita sudah tahu bahwa sebuah fungsi bisa mengakses variabel diluar dari fungsi tersebut (variabel \"luar\").\n\nTapi apa yang terjadi jika variabel luar berubah saat fungsinya dibuat? Akankan fungsinya mendapatkan nilai yang baru atau yang lama?\n\nDan bagaimana jika sebuah fungsi diberikan sebagai paramter dan dipanggil dibagian kode lain, akankah itu mendapatkan akses ke variabel luar ditempat itu?\n\nAyo kita peruas pengetahuan kita untuk mengerti skenario ini dan skenario yang lebih kompleks.\n\n```smart header=\"Kita akan bahas tentang variabel `let/const` di sini\"\nDi JavaScript, ada 3 cara mendeklarasi variabel: `let`, `const` (cara-cara modern), dan `var` (sisa masa lalu).\n\n- Di artikel ini kita akan memakai variabel `let` dalam contoh.\n- Variabel, yang dideklarasi dengan `const`, bertindak sama, jadi artikel ini juga tentang `const`.\n- `var` usang punya perbedaan mencolok, mereka akan dibahas di artikel <info:var>.\n```\n\n## Blok kode\n\nJika variabel dideklarasi di dalam blok kode `{...}`, ia hanya terlihat di dalam blok itu.\n\nMisalnya:\n\n```js run\n{\n  // lakukan pekerjaan dengan variabel lokal yang harusnya tak terlihat dari luar\n\n  let message = \"Hello\"; // hanya terlihat dalam blok ini\n\n  alert(message); // Hello\n}\n\nalert(message); // Error: message is not defined\n```\n\nKita bisa memakai ini untuk mengisolasi potongan kode yang melakukan tugasnya sendiri, dengan variabel yang dia punya sendiri:\n\n```js run\n{\n  // tampilkan pesan\n  let message = \"Hello\";\n  alert(message);\n}\n\n{\n  // tampilkan pesan lain\n  let message = \"Goodbye\";\n  alert(message);\n}\n```\n\n````smart header=\"Akan muncul galat tanpa blok\"\nTolong ingat, tanpa blok terpisah akan muncul galat, jika kita memakai `let` dengan nama variabel yang sudah ada:\n\n```js run\n// tampilkan pesan\nlet message = \"Hello\";\nalert(message);\n\n// tampilkan pesan lain\n*!*\nlet message = \"Goodbye\"; // Galat: variabel sudah dideklarasi\n*/!*\nalert(message);\n```\n````\n\nUntuk `if`, `for`, `while` dan lain-lain, variabel yang dideklarasi dalam `{...}` juga hanya terlihat di situ saja:\n\n```js run\nif (true) {\n  let phrase = \"Hello!\";\n\n  alert(phrase); // Hello!\n}\n\nalert(phrase); // Galat, variabel ini tak ada!\n```\n\nDi sini, setelah `if` selesai, `alert` di bawah tak akan melihat `phrase`, sehingga terjadi galat.\n\nIni keren, karena ia memperbolehkan kita membuat variabel blok-lokal, yang spesifik ke cabang `if`.\n\nHal serupa juga berlaku untuk loop `for` dan `while`:\n\n```js run\nfor (let i = 0; i < 3; i++) {\n  // variabel i hanya terlihat di dalam for ini\n  alert(i); // 0, lalu 1, lalu 2\n}\n\nalert(i); // Galat, variabel ini tak ada\n```\n\nVisually, `let i` is outside of `{...}`. But the `for` construct is special here: the variable, declared inside it, is considered a part of the block.\n\n## Fungsi bersarang\n\nSebuah fungsi dikatakan \"bersarang\" apabila fungsi tersebut dibuat di dalam fungsi lainnya.\n\nHal tersebut mudah untuk dilakukan di JavaScript.\n\nKita dapat melakukannya untuk mengatur kode kita, seperti ini:\n\n```js\nfunction sayHiBye(firstName, lastName) {\n\n  // fungsi pembantu untuk digunakan di bawah\n  function getFullName() {\n    return firstName + \" \" + lastName;\n  }\n\n  alert( \"Hello, \" + getFullName() );\n  alert( \"Bye, \" + getFullName() );\n\n}\n```\n\nDi sini fungsi *bersarang* dibuat untuk kemudahan. Fungsi tersebut bisa mengakses variabel luar sehingga dapat mengembalikan nama lengkap. Fungsi bersarang cukup sering ditemui di JavaScript.\n\nYang lebih menarik yaitu, fungsi bersarang dapat dikembalikan: bisa sebagai properti dari objek baru atau sebagai nilai kembalian itu sendiri. Nilai kembalian tersebut bisa dipakai di tempat lain. Tak peduli di mana, ia masih punya akses ke variabel luar yang sama.\n\nDi bawah ini, `makeCounter` membuat fungsi \"counter\" yang mengembalikan angka berikutnya di tiap invokasi:\n\n```js run\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\nalert( counter() ); // 2\n```\n\nMeski sederhana, varian kode itu yang sedikit dimodifikasi punya kegunaan praktis, misalnya, sebagai [generator angka random](https://en.wikipedia.org/wiki/Pseudorandom_number_generator) untuk menggenerate nilai random untuk tes terotomasi.\n\nHow does this work? If we create multiple counters, will they be independent? What's going on with the variables here?\n\nMemahami hal begini bagus untuk pengetahuan keseluruhan JavaScript dan menguntungkan untuk skenario yang lebih komplex. Jadi ayo kita selami lebih dalam.\n\n## Lingkungan Lexikal\n\n```warn header=\"Sini jadilah naga!\"\nPenjelasan teknikal mendalam ada di depan.\n\nSemakin jauh aku menghindari detil bahasa level-rendah, pemahaman apapun tanpa mereka akan kekurangan dan tak-lengkap, jadi bersiaplah.\n```\n\nSupaya jelas, penjelasan dibagi dalam beberapa langkah.\n\n### Langkah 1. Variabel\n\nDi JavaScript, setiap fungsi yang berjalan, blok kode `{...}`, dan satu script yang menyeluruh punya objek internal (tersembunyi) yang terasosiasi yang dikenal dengan *Lingkungan Lexikal*.\n\nObjek Lingkungan Lexikal punya dua bagian:\n\n1. *Rekaman Lingkungan* -- objek yang menyimpan semua variabel lokal sebagai propertinya (dan beberapa informasi lain seperti nilai `this`).\n2. Referensi ke *lingkungan lexikal luar*, yang terasosiasi dengan kode luar.\n\n**\"Variabel\" cuma suatu properti dari objek internal spesial, `Rekaman Lingkungan`. \"Untuk memperoleh atau mengganti variabel\" berarti \"memperoleh atau mengganti properti dari objek itu\".**\n\nDi kode sederhana tanpa fungsi ini, cuma ada satu Lingkugan Lexikal:\n\n![lingkungan lexikal](lexical-environment-global.svg)\n\nIni yang disebut Lingkungan Lexikal *global*, terasosiasi dengan script keseluruhan.\n\nDi gambar di atas, kotak persegi panjang artinya Rekaman Lingkungan (simpanan variabel) dan panah artinya referensi luar. Lingkungan Lexikal global tak punya referensi luar, itulah kenapa panahnya menunjuk ke `null`.\n\nSeiring kodenya mulai bereksekusi dan berjalan, Lingkungan Lexikal berganti.\n\nIni kode yang sedikit lebih panjang:\n\n![lingkungan lexikal](closure-variable-phrase.svg)\n\nKotak persegi panjang di sisi kanan mendemonstrasikan bagaimana Lingkungan Lexikal global berganti selama exekusi:\n\n1. Ketika script berjalan, Lingkungan Lexikal di-pre-populasi dengan semua variabel yang terdeklarasi.\n    - Awalnya, mereka di state \"Belum terinisialisir\". Itu state internal spesial, yang berarti bahwa engine tahu tentang variabelnya, tapi tak akan mengijinkan penggunaan itu sebelum `let`. Ini hampir sama saja dengan variabel itu tak ada.\n2. Lalu definisi `let phrase` muncul. Tak ada penetapan dulu, jadi nilainya `undefined`. Kita sudah bisa pakai variabel ini di momen ini.\n3. `phrase` diberikan nilai.\n4. `phrase` mengganti nilai.\n\nApapun terlihat simpel untuk sekarang, ya kan?\n\n- Variabel ialah properti dari objek internal spesial, yang terasosiasi dengan blok/fungsi/script yang sedang berexekusi.\n- Bekerja dengan variabel sebenarnya bekerja dengan properti objek itu.\n\n```smart header=\"Lingkungan Lexikal merupakan objek spesifikasi\"\n\"Lingkungan Lexikal\" ialah objek spesifikasi: ia cuma ada \"secara teori\" di [spesifikasi bahasa](https://tc39.es/ecma262/#sec-lexical-environments) untuk menjelaskan bagaimana cara ia bekerja. Kita tak bisa memperoleh objek ini di kode kita dan memanipulasinya langsung.\n\nEngine JavaScript juga bisa mengoptimisasi itu, menghapus variabel yang tak dipakai untuk menghemat memory dan melakukan trik internal lainnya, selama kelakuan yang terlihat sesuai deskripsi.\n```\n\n### Langkah 2. Deklarasi Fungsi\n\nFungsi juga berupa nilai, seperti variabel.\n\n**Bedanya ialah Deklarasi Fungsi terinisialisasi penuh secara instan.**\n\nKetika Lingkungan Lexikal dibuat, Deklarasi Fungsi segera menjadi fungsi siap-pakai (tak seperti `let`, yang tak bisa dipakai hingga deklarasi).\n\nItulah kenapa kita bisa memakai fungsi, yang dideklarasi sebagai Deklarasi Fungsi, bahkan sebelum deklarasinya itu sendiri.\n\nMisalnya, ini state awal dari Lingkungan Lexikal global ketika kita tambah satu fungsi:\n\n![](closure-function-declaration.svg)\n\nAlaminya, kelakukan ini cuma berlaku pada Deklarasi Fungsi, bukan Expresi Fungsi di mana kita menetapkan fungsi ke variabel, seperti `let say = function(name)...`.\n\n### Langkah 3. Lingkungan Lexikal dalam dan luar\n\nKetika satu fungsi berjalan, di awal panggilan, Lingkungan Lexikal tercipta otomatis untuk menyimpan variabel lokal dan parameter dari panggilannya.\n\nMisalnya, untuk `say(\"John\")`, ini seperti (exekusinya ada di baris tersebut, yang diberi label dengan panah):\n\n<!--\n    ```js\n    let phrase = \"Hello\";\n\n    function say(name) {\n     alert( `${phrase}, ${name}` );\n    }\n\n    say(\"John\"); // Hello, John\n    ```-->\n\n![](lexical-environment-simple.svg)\n\nSelama panggilan fungsi, kita punya dua Lingkungan Lexikal: dalam (untuk panggilan fungsi) dan luar (global):\n\n- Lingkungan Lexikal dalam berkorespondensi dengan exekusi `say` yang sedang berlangsung. Ia punya properti tunggal: `name`, argumen fungsi. Kita panggil `say(\"John\")`, jadi nilai `name` adalah `\"John\"`.\n- Lingkungan Lexikal luar ialah Lingkungan Lexikal global. Ia punya variabel `phrase` dan fungsinya itu sendiri.\n\nLingkungan Lexikal dalam punya referensi ke `outer`.\n\n**Ketika kode ingin mengakses variabel -- Lingkungan Lexikal dalam ditelusuri pertama, lalu terluar, lalu yang lebih terluar dan berikutnya.**\n\nJika variabel tak ditemukan di manapun, itu adalah galat dalam mode ketat (tanpa `use strict`, penetapan ke variabel yang tak pernah ada menciptakan satu variabel global, untuk kompatibilitas dengan kode usang).\n\nDi contoh ini penelusuran terjadi seperti berikut:\n\n- Untuk variabel `name`, `alert` di dalam `say` mencarinya segera di dalam Lingkungan Lexikal dalam.\n- Ketika ia ingin mengakses `phrase`, maka tak ada `phrase` secara lokal, jadi ia mengikuti referensi ke Lingkungan Lexikal luar dan menemui itu di sana.\n\n![lexical environment lookup](lexical-environment-simple-lookup.svg)\n\n\n### Langkah 4. Mengembalikan fungsi\n\nAyo kembali ke contoh `makeCounter`.\n\n```js\nfunction makeCounter() {\n  let count = 0;\n\n  return function() {\n    return count++;\n  };\n}\n\nlet counter = makeCounter();\n```\n\nDi awal tiap panggilan `makeCounter()`, objek Lingkungan Lexikal baru dibuat, untuk menyimpan variabel untuk perjalanan `makeCounter` ini.\n\nJadi kita punya dua Lingkungan Lexikal bersarang, sama seperti contoh di atas:\n\n![](closure-makecounter.svg)\n\nBedanya adalah, selama exekusi dari `makeCounter()`, fungsi kecil bersarang tercipta dari cuma satu baris: `return count++`. Kita tak menjalankan itu sekarang, cuma membuat.\n\nSemua fungsi mengingat Lingkungan Lexikal di mana mereka dibuat. Teknisnya, tak ada sihir di sini: semua fungsi punya properti tersembunyi bernama `[[Environment]]`, yang menyimpan referensi ke Lingkungan Lexikal di mana fungsi itu dibuat:\n\n![](closure-makecounter-environment.svg)\n\nJadi, `counter.[[Environment]]` punya referensi ke `{count: 0}` Lingkungan Lexikal. Itulah bagaimana fungsi mengingat di mana ia dibuat, tak peduli di mana ia dipanggil. Referensi `[[Environment]]` diset sekali dan selamanya saat kresi fungsi.\n\nLalu, saat `counter()` dipanggil, Lingkungan Lexikal baru dibuat untuk panggilan, dan referensi Lingkungan Lexikal luar-nya diambil dari `counter.[[Environment]]`:\n\n![](closure-makecounter-nested-call.svg)\n\nSekarang ketika kode di dalam `counter()` mencari variabel `count`, ia pertama memeriksa Lingkungan Lexikal miliknya sendiri (kosong, karena tak ada variabel lokal di sana), lalu Lingkungan Lexikal dari panggilan `makeCounter()` luar, di mana ia ditemukan dan berubah.\n\n**Variabel diperbarui di Lingkungan Lexikal di mana ia tinggal.**\n\nIni state setelah exekusi:\n\n![](closure-makecounter-nested-call-2.svg)\n\nJika kita panggil `counter()` beberapa kali, variabel `count` akan meningkat ke `2`, `3`, dan seterusnya, at the same place.\n\n```smart header=\"Closure\"\nAda satu istilah pemrograman umum \"closure\", yang sebaiknya diketahui developer secara umum.\n\n[Closure](https://en.wikipedia.org/wiki/Closure_(computer_programming)) ialah fungsi yang mengingat variabel luarnya dan bisa mengakses mereka. Di beberapa bahasa, itu tak mungkin, atau satu fungsi harus ditulis dalam cara spesial untuk membuat ini terjadi. Tapi seperti yang dijelaskan di atas, di JavaScript, semua fungsi alaminya adalah closure (cuma ada satu pengecualian, akan dibahas di <info:new-function>).\n\nYaitu: mereka otomatis mengingat di mana mereka dibuat menggunakan property `[[Environment]]` tersembunyi, kemudian kdoe mereka bisa mengakses variabel luar.\n\nKetika dalam interview, frontend developer mendapat pertanyaan tentang \"apa itu closure?\", jawaban valid yaitu definisi closure dan penjelesan bahwa semua fungsi di JavaScript adalah closure, dan mungkin sedikit kata-kata tentang detil teknis: properti `[[Environment]]` dan bagaimana Lingkungan Lexikal bekerja.\n```\n\n## Koleksi sampah\n\nBiasanya, Lingkungan Lexikal dihapus dengan semua variabel setelah panggilan fungsinya selesai. Ini karena tak ada referensi ke situ. Sebagai objek JavaScript apapun, ia cuma ditahan di memory selama ia dapat digapai.\n\n...Tapi jika ada fungsi bersarang yang masih dapat digapai setelah akhir fungsi, maka ia punya properti `[[Environment]]` yang mereferensi lingkungan lexikal.\n\nDalam hal Lingkungan Lexikal masih bisa digapai meski setelah berakhirnya fungsi itu, ia tetap hidup.\n\nMisalnya:\n\n```js\nfunction f() {\n  let value = 123;\n\n  return function() {\n    alert(value);\n  }\n}\n\nlet g = f(); // g.[[Environment]] menyimpan referensi ke Lingkungan Lexikal\n// dari panggilan f() yang sesuai\n```\n\nTolong diperhatikan apabila `f()` dipanggil beberapa kali, dan fungsi kembaliannya disimpan, maka seluruh objek lingkungan leksikal akan disimpan di memori. Ketiga-tiganya pada kode di bawah:\n\n```js\nfunction f() {\n  let value = Math.random();\n\n  return function() { alert(value); };\n}\n\n// 3 fungsi di array, semuanya terhubung ke lingkungan leksikal\n// dari setiap f() yang bersangkutan\nlet arr = [f(), f(), f()];\n```\n\nSebuah objek lingkungan leksikal mati apabila sudah tidak dapat dicapai (sperti objek lainnya). Dengan kata lain, objek tersebut hidup selama masih ada setidaknya satu fungsi bersarang yang mengacunya.\n\nDi kode berikut, setelah fungsi bersarang itu dihapus, Lingkungan Lexikal lingkupannya (serta `value`-nya) dibersihkan dari memori;\n\n```js\nfunction f() {\n  let value = 123;\n\n  return function() {\n    alert(value);\n  }\n}\n\nlet g = f(); // selama fungsi g tetap ada, nilainya tetap berada di memori\n\ng = null; // ...dan sekarang memori dibersihkan\n```\n\n### Optimalisasi kehidupan nyata\n\nSeperti yang kita lihat, di teori selama sebuah fungsi masih hidup, seluruh variabel luarnya juga disimpan.\n\nTetapi di praktiknya, mesin JavaScript mencoba untuk mengoptimalkannya. Mereka menganalisis penggunaan variabel dan apabila sudah jelas bahwa variabel luar sudah tidak digunakan -- mereka dihapus.\n\n**Sebuah efek samping yang penting di V8 (Chrome, Opera) adalah variabel akan tak dapat diakses saat debugging**\n\nCobalah jalankan contoh di bawah di Chrome dengan Developer Tools.\n\nSaat dihentikan, pada console coba ketikkan `alert(value)`.\n\n```js run\nfunction f() {\n  let value = Math.random();\n\n  function g() {\n    debugger; // di console: ketik alert(value); Variabel tak ditemukan!\n  }\n\n  return g;\n}\n\nlet g = f();\ng();\n```\n\nSeperti yang kita lihat -- variabel tersebut tak ditemukan! Secara teori, variabel tersebut masih bisa diakses, tetapi mesin mengoptimalkannya.\n\nHal tersebut mungkin menyebabkan masalah debugging yang aneh (mungkin memakan waktu). Salah satunya -- apabila kita mendapat variabel luar yang tak diharapkan:\n\n```js run global\nlet value = \"Surprise!\";\n\nfunction f() {\n  let value = \"the closest value\";\n\n  function g() {\n    debugger; // di console: ketik alert(value); Surprise!\n  }\n\n  return g;\n}\n\nlet g = f();\ng();\n```\n\nFitur V8 ini baik untuk diketahui. Jika kamu melakukan debug memakai Chrome/Opera, cepat atau lambat kamu akan menemuinya.\n\nIni bukan bug di debugger, melainkan fitur spesial dari V8. Mungkin ini akan diganti suatu saat.\nKamu bisa mengeceknya dengan menjalankan contoh di laman ini.\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/04-var/article.md",
    "content": "\n# Si Tua \"var\"\n\n```smart header=\"Artikel ini untuk memahami script lama\"\nInformasi yang terdapat di artikel ini berguna untuk memahami script lama.\nHal itu bukanlah cara kita menulis kode baru.\n```\n\nDi bab paling awal tentang [variabel](info:variables), kami menyebutkan tiga cara untuk deklarasi variabel:\n1. `let`\n2. `const`\n3. `var`\n\nDeklarasi dari `var` sama dengan `let`. Kebanyakan kasus kita bisa mengganti `let` dengan `var` atau sebaliknya dan dan mengira itu akan berjalan lancar:\n\n```js run\nvar message = \"Hi\";\nalert(message); // Hi\n```\n\nTapi secara internal `var` adalah monster yang benar-benar berbeda, yang berasal dari masa lalu. `var` biasanya tidak digunakan didalam skrip modern, tapi masih tetap ada didalam skrip-skrip lama.\n\nJika kamu tidak berencana bertemu dengan skrip seperti itu kamu bisa melewati bab ini dan membacanya nanti.\n\nAkan tetapi, perbedaan ini sangatlah penting untuk dimengerti apalagi ketika mengubah skrip lama dari `var` menjadi `let`. untuk menghindari error-error yang aneh.\n\nVariabel, dideklarasikan dengan `var`, baik function-wide ataupun global. Mereka terlihat melalui blok.\n\nContohnya:\n\n\n```js run\nif (true) {\n  var test = true; // gunakan \"var\" daripada \"let\"\n}\n\n*!*\nalert(test); // benar, variabel ada setelah if\n*/!*\n```\n\nKarena `var` mengabaikan blok kode , kita mendapatkan global variabel `test`.\n\nJika kita menggunakan `let test` daripada `var test`, maka variabel hanya akan terlihat di dalam `if`:\n\n```js run\nif (true) {\n  let test = true; // gunakan \"let\"\n}\n\n*!*\nalert(test); // Error: test tidak didefinisikan\n*/!*\n```\n\nHal yang sama juga untuk loop: `var` tidak dapat berupa blok atau loop-lokal:\n\n```js\nfor (var i = 0; i < 10; i++) {\n  var one = 1;\n  // ...\n}\n\n*!*\nalert(i); // 10, \"i\" terlihat setelah loop, itu adalah global variabel\n*/!*\n```\n\nJika blok kode ada di dalam fungsi, maka `var` menjadi variabel tingkat fungsi:\n\n```js run\nfunction sayHi() {\n  if (true) {\n    var phrase = \"Hello\";\n  }\n\n  alert(phrase); // bekerja\n}\n\nsayHi();\n\nalert(phrase); // Error: frasa tidak terdefinisi (periksa Developer Console)\n\n```\n\nSeperti yang bisa kita lihat `var` menembus `if`, `for` atau blok kode lainnya. Itu karena sejak dahulu di blok Javascript tidak memiliki Lingkungan Leksikal. dan `var` adalah sisanya. \n\n## \"var\" mentoleransi pendeklarasian ulang\n\nJika kita mendeklarasikan variabel yang sama dengan `let` didalam scope yang sama, itu akan menciptakan error:\n\n```js run\nlet user;\nlet user; // SyntaxError: 'user' has already been declared\n```\n\nDengan `var`, kita bisa mendeklarasikan ulang berapa kalipun. Jika kita menggunakan `var` dengan variabel yang telah dideklarasikan, `var` itu akan diabaikan:\n\n```js run\nvar user = \"Pete\";\n\nvar user = \"John\"; // \"var\" ini tidak melakukan apapun (sudah dideklarasikan)\n// ...hal ini tidak akan menciptakan error\n\nalert(user); // John\n```\n\n## Variabel \"var\" bisa dideklarasikan dibawah penggunaan variabel tersebut\n\nDeklarasi `var` diproses ketika fungsi dimulai (atau skrip dijalankan untuk global).\n\nDengan kata lain, variabel `var` didefinisikan dari awal fungsi, tidak peduli di manapun definisi tersebut ( dengan asumsi definisi tidak didalam fungsi bersarang).\nJadi kode ini:\n\n```js run\nfunction sayHi() {\n  phrase = \"Hello\";\n\n  alert(phrase);\n\n*!*\n  var phrase;\n*/!*\n}\nsayHi();\n```\n\n...Secara teknis sama dengan ini (memindahkan `var pharse` di atas):\n\n```js run\nfunction sayHi() {\n*!*\n  var phrase;\n*/!*\n\n  phrase = \"Hello\";\n\n  alert(phrase);\n}\nsayHi();\n```\n\n...Atau bahkan seperti ini (Ingat, blok kode diabaikan):\n\n```js run\nfunction sayHi() {\n  phrase = \"Hello\"; // (*)\n\n  *!*\n  if (false) {\n    var phrase;\n  }\n  */!*\n\n  alert(phrase);\n}\nsayHi();\n```\n\nOrang-orang juga menyebut perilaku seperti ini \"hoisting\", karena semua `var` \"hoisted\" (diangkat) ke bagian atas fungsi.\n\nSehingga dalam contoh di atas, cabang `if (false)` tidak pernah dijalankan, tetapi itu tidak masalah. `var` di dalamnya diproses di awal fungsi, jadi pada saat `(*)` variabel ada. \n**Pendeklarasian hoisted, sedangkan penugasan (assigment) tidak.**\n\nLebih baik didemonstrasikan dengan sebuah contoh:\n\n```js run\nfunction sayHi() {\n  alert(phrase);  \n\n*!*\n  var phrase = \"Hello\";\n*/!*\n}\n\nsayHi();\n```\n\nPada baris `var pharse = \"Hello\"` memiliki dua aksi didalamnya:\n1. Deklarasi variabel `var`\n2. Penugasan variabel `=`.\n\nDeklarasi diproses pada awal pelaksanaan fungsi (\"hoisted\"), tetapi penugasan selalu bekerja di tempat mucul. sehingga pada dasarnya kode bekerja seperti ini: \n```js run\nfunction sayHi() {\n*!*\n  var phrase; // deklarasi bekerja di awal...\n*/!*\n\n  alert(phrase); // tidak terdefinisi\n\n*!*\n  phrase = \"Hello\"; // ...penugasan - saat penugasan mencapainya.\n*/!*\n}\n\nsayHi();\n```\n\nKarena semua deklarasi `var` diproses pada awal fungsi, kita dapat mendeferensikanya dimana saja. tetapi variabel tidak terdefinisi sampai penugasan.\n\nDalam kedua contoh diatas `alert` bekerja tanpa error, karena ada variabel `pharse`. Tetapi karena nilainya belum ditetapkan, sehingga menampilkan `undefined`.\n\n## IIFE\n\nKarena di masa lalu hanya ada `var`, dan ia tidak memiliki visibilitas tingkat blok, programmer menemukan cara untuk menirunya. cara mereka melakukanya dinamakan “immediately-invoked function expressions” (disingkat IIFE). \nItu bukanlah sesuatu yang harus kita gunakan saat ini, tetapi anda dapat menemukannya di skip lama\nSebuah IIFE terlihat seperti ini:\n\n```js run\n(function() {\n\n  var message = \"Hello\";\n\n  alert(message); // Hello\n\n})();\n```\n\nDisini ekspresi fungsi dibuat dan segera dipangil. Sehingga kode dieksekusi segera dan memiliki variabel pribadi sendiri.\n\nFungsi ekspresi dibungkus dengan tanda kurung `(function {...})`, karena ketika Javascript bertemu `\"function\"` dalam aliran kode utama, ia memahaminya sebagai awal dari Deklarasi Fungsi. tetapi sebuah Deklarasi Fungsi harus memiliki nama, sehingga kode seperti ini akan menghasilkan error:\n```js run\n// mencoba untuk mendeklarasikan dan langsung memanggil fungsinya\nfunction() { // <-- Error: Statmen Function membutuhkan sebuah nama fungsi\n\n  var message = \"Hello\";\n\n  alert(message); // Hello\n\n}();\n```\n\nBahkan jika kita mengatakan: \"Ok, mari tambahkan nama\", hal itu tidak dapat bekerja, karena Javascript tidak mengizinkan Deklarasi Fungsi dipanggil segera:\n```js run\n// syntax error karena frasa dibawah\nfunction go() {\n\n}(); // <-- tidak dapat segera memanggil deklarasi fungsi\n```\n\nJadi, tanda kurung di sekitar fungsi adalah trik untuk menunjukan Javascript bahwa fungsi dibuat dalam konteks ekxpresi lain, dan karenanya merupakan ekspresi fungsi: tidak memerlukan nama dan segera dipanggil.\n\nAda beberapa cara lain selain tanda kurung untuk memberi tahu Javascript bahwa yang dimaksud adalah Ekspresi fungsi:\n\n```js run\n// Cara membuat IIFE\n\n(function() {\n  alert(\"kurung disekitar fungsi\");\n}*!*)*/!*();\n\n(function() {\n  alert(\"kurung disekitar semuanya\");\n}()*!*)*/!*;\n\n*!*!*/!*function() {\n  alert(\"Operator Bitwise NOT memulai ekspresi\");\n}();\n\n*!*+*/!*function() {\n  alert(\"Unary plus memulai ekspresi\");\n}();\n```\n\nDalam semua kasus diatas kami mendeklarasikan sebuah Ekspresi fungsi dan menjalankanya segera. Mari catat kembali: Saat ini tidak ada alasan untuk menulis kode seperti itu.\n\n## Kesimpulan\n\nAda dua perbedaan utama dari `var` dibandingkan dengan `let/const`;\n\n1. `var` variabel tidak memiliki ruang lingkup blok, mereka terlihat minimum pada tingkat fungsi.\n2. Deklarasi `var` diproses saat fungsi dimulai (skrip dimulai untuk global).\n\nAda satu perbedaan kecil terkait objek global, yang akan kita bahas pada bab selanjutnya.\n\nPerbedaan-perbedaan ini membuat `var` lebih buruk daripada` let` hampir di setiap waktu. Variabel block-level adalah hal yang bagus. Itu sebabnya `let` diperkenalkan dalam standar sejak dahulu, dan sekarang merupakan cara utama (bersama dengan` const`) untuk mendeklarasikan variabel.\n"
  },
  {
    "path": "1-js/06-advanced-functions/05-global-object/article.md",
    "content": "\n# Objek global\n\nObjek global menyediakan variabel dan fungsi yang bisa didapatkan dimana saja. Secara default, variabel dan fungsi yang sudah berada didalam bahasanya atau lingkungannya.\n\nDi dalam browser ia dinamakan `window`, untuk Node.js `global`, untuk lingkungan lainnya ia mungkin mempunyai nama lain.\n\nAkhir-akhir ini, `globalThis` ditambahkan ke bahasanya, sebagai nama standar untuk objek global, yang harus di dukung di semua lingkungan. Di browser tertentu, ya itu non-Chromium Edge, `globalThis` belum didukung, tapi bisa dengan mudah dipolyfill.\n\nKita akan memakai `window` disini, dengan anggapan bahwa lingkungan kita adalah browser. Jika script kamu mungkin digunakan di lingkungan lain, lebih baik menggunakan `globalThis`.\n\nSemua properti objek global bisa diakses secara langsung:\n\n```js run\nalert(\"Hello\");\n// sama saja dengan\nwindow.alert(\"Hello\");\n```\n\nDi dalam browser, fungsi global dan variabel yang dinyatakan dengan `var` (bukan `let/const`!) menjadi properti global objek:\n\n```js run untrusted refresh\nvar gVar = 5;\n\nalert(window.gVar); // 5 (menjadi properti objek global)\n```\n\nMohon jangan bergantung dengan itu! Perilaku ini ada untuk alasan kompatibilitas. Script modern menggunakan [JavaScript modules](info:modules) dimana hal-hal tersebut tidak terjadi.\n\nJika kita menggunakan `let`, hal tersebut tidak akan terjadi:\n\n```js run untrusted refresh\nlet gLet = 5;\n\nalert(window.gLet); // undefined (tidak menjadi properti objek global)\n```\n\nJika sesuatu nilai sangat penting sesampai kamu ingin membuatnya tersedia secara global, tulislah langsung sebagai satu properti:\n\n```js run\n*!*\n// buat info pengguna saat ini global, supaya semua script bisa mengaksesnya\nwindow.currentUser = {\n  name: \"John\"\n};\n*/!*\n\n// di tempat lain di kode\nalert(currentUser.name);  // John\n\n// atau jika kita mempunyai variabel lokal dengan nama \"currentUser\"\n// ambillah secara eksplisit dari window (aman!)\nalert(window.currentUser.name); // John\n```\n\nMeskipun begitu, menggunakan variabel global umumnya tidak dianjurkan. Variabel global harus ada sesedikit mungkin. Desain kode dimana fungsi mendapatkan variabel \"input\" dan mengeluarkan \"outcome\" tertentu akan lebih jelas, kurang cenderung menghasilkan eror dan lebih mudah untuk dites dibanding jika ia menggunakan variabel luar atau global.\n\n\n## Menggunakan polyfills\n\nKita menggunakan objek global untuk mengetes dukungan atas fitur bahasa modern.\n\nContohnya, tes jika objek built-in `Promise` berada (tidak ada di browser yang sangat tua):\n```js run\nif (!window.Promise) {\n  alert(\"Your browser is really old!\");\n}\n```\n\nJika tidak ada (anggap kita di browser tua), kita bisa menciptakan \"polyfills\": tambahkan fungsi yang tidak di dukung oleh lingkungan, tapi ada di standar modern.\n\n```js run\nif (!window.Promise) {\n  window.Promise = ... // implementasi custom dari fitur bahasa modern\n}\n```\n\n## Ringkasan\n\n- Objek global menyimpan variabel yang harus tersedia dimana saja.\n    Itu termasuk built-in Javascript, seperti `Array` dan nilai-nilai lingkungan-spesifik, seperti `window.innerHeight` -- tinggi window di dalam browser.\n- Objek global mempunyai nama universal `globalThis`.\n    ...Tapi lebih sering disebut nama lingkungan-spesifik \"old-school\", seperti `window` (browser) dan `global` (Node.js).  Karena `globalThis` adalah usulan baru, ia belum didukung di dalam non-Chromium Edge (tapi bisa dipolyfill).\n- Kita harus menyimpan nilai di objek global jika kalau ia benar-benar global untuk projek kita. Dan pertahankan jumlah minimum. \n- Dalam browser, jika kita tidak menggunakan [modules](info:modules), fungsi global dan variabel ternyatakan `var` menjadi properti objek global.\n- Untuk membuat kode kami future-proof dan lebih mudah dimengerti, kita harus mengakses properti dari global objek secara langsung, sebagain `window.x`.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/solution.js",
    "content": "function makeCounter() {\n  let count = 0;\n\n  function counter() {\n    return count++;\n  }\n\n  counter.set = value => count = value;\n\n  counter.decrease = () => count--;\n\n  return counter;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/source.js",
    "content": "function makeCounter() {\n  let count = 0;\n\n  // ... kodemu ...\n}\n\nlet counter = makeCounter();\n\nalert( counter() ); // 0\nalert( counter() ); // 1\n\ncounter.set(10); // setel perhitungan baru\n\nalert( counter() ); // 10\n\ncounter.decrease(); // kurangi perhitungannya dengan 1\n\nalert( counter() ); // 10 (daripada 11)\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/_js.view/test.js",
    "content": "describe(\"counter\", function() {\n\n  it(\"increases from call to call\", function() {\n\n    let counter = makeCounter();\n\n    assert.equal( counter(), 0 ); \n    assert.equal( counter(), 1 ); \n    assert.equal( counter(), 2 ); \n  });\n\n  \n  describe(\"counter.set\", function() {\n    it(\"sets the count\", function() {\n\n      let counter = makeCounter();\n\n      counter.set(10);\n\n      assert.equal( counter(), 10 ); \n      assert.equal( counter(), 11 ); \n    });\n  });\n  \n  describe(\"counter.decrease\", function() {\n    it(\"decreases the count\", function() {\n\n      let counter = makeCounter();\n\n      counter.set(10);\n\n      assert.equal( counter(), 10 ); \n\n      counter.decrease();\n\n      assert.equal( counter(), 10 ); \n\n    });\n  });\n\n});"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/solution.md",
    "content": "\nSolusinya adalah menggunakan `count` didalam variabel lokal, tapi metode tambahan ditulis tepat didalam `counter`nya. Mereka membagi lingkungan leksikan luar yang sama dan juga bisa mengakses `count` yang sekarang.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/2-counter-inc-dec/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Setel dan kurangi penghitung\n\nModifikasi kode dari `makeCounter()` jadi penghitungnya juga bisa mengurangi dan menyetel ulang angkanya:\n\n- `counter()` harus mengembalikan angka selanjutnya (seperti sebelumnya).\n- `counter.set(value)` harus menyetel ulang penghitungnya jadi `value`.\n- `counter.decrease()` harus mengurangi angka penghitungnya dengan 1.\n\nLihat kode pada sandbox untuk contoh penggunaan yang lengkap.\n\nCatatan. Kamu bisa menggunakan closure atau properti fungsi untuk menyimpan perhitungan yang sekarang. Atau tulis kedua variannya.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js",
    "content": "function sum(a) {\n\n  let currentSum = a;\n\n  function f(b) {\n    currentSum += b;\n    return f;\n  }\n\n  f.toString = function() {\n    return currentSum;\n  };\n\n  return f;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js",
    "content": "function sum(a){\n  // Tulis kodemu disini.\n\n}\n\n/*\nsum(1)(2) == 3; // 1 + 2\nsum(1)(2)(3) == 6; // 1 + 2 + 3\nsum(5)(-1)(2) == 6\nsum(6)(-1)(-2)(-3) == 0\nsum(0)(1)(2)(3)(4)(5) == 15\n*/\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/test.js",
    "content": "describe(\"sum\", function(){\n  \n  it(\"sum(1)(2) == 3\", function(){\n    assert.equal(3, sum(1)(2));\n  });\n\n  it(\"sum(5)(-1)(2) == 6\", function(){\n    assert.equal(6, sum(5)(-1)(2));\n  });\n  \n  it(\"sum(6)(-1)(-2)(-3) == 0\", function(){\n    assert.equal(0, sum(6)(-1)(-2)(-3));\n  });\n\n  it(\"sum(0)(1)(2)(3)(4)(5) == 15\", function(){\n    assert.equal(15, sum(0)(1)(2)(3)(4)(5));\n  });\n});\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/solution.md",
    "content": "\n1. Untuk membuat semuanya bekerja *entah bagaimana*, hasil dari `sum` haruslah sebuah fungsi.\n2. Fungsi harus menyimpan nilai sekarang yang berada diantara pemanggilan didalam memori.\n3. Menurut tasknya, fungsinya harus menjadi angka ketika digunakan didalam `==`. Fungsi adalah objek, jadi perubahan terjasi seperti yang dideskripsikan didalam bab <info:object-toprimitive>, dan kita bisa menyediakan metode milik kita yang mengembalikan angkanya.\n\nSekarang angkanya:\n\n```js demo run\nfunction sum(a) {\n\n  let currentSum = a;\n\n  function f(b) {\n    currentSum += b;\n    return f;\n  }\n\n  f.toString = function() {\n    return currentSum;\n  };\n\n  return f;\n}\n\nalert( sum(1)(2) ); // 3\nalert( sum(5)(-1)(2) ); // 6\nalert( sum(6)(-1)(-2)(-3) ); // 0\nalert( sum(0)(1)(2)(3)(4)(5) ); // 15\n```\n\nPerhatikan baik-baik bahwa fungsi `sum` sebenarnya hanya bekerja satu kali. Itu mengembalikan fungsi `f`.\n\nLalu, untuk setiap pemanggilan selanjutnya `f` menambahkan parameternya kedlaam `currentSum`, dan mengembalikan dirinya sendiri.\n\n**Tidak terdapat rekursi di akhir baris dari `f`.**\n\nIni adalah bagaimana rekursi terlihat:\n\n```js\nfunction f(b) {\n  currentSum += b;\n  return f(); // <-- pemanggilan rekursi\n}\n```\n\nDan didalam kasus kita, kita hanya mengembalikan fungsinya, tanpa memanggilnya:\n\n```js\nfunction f(b) {\n  currentSum += b;\n  return f; // <-- tidak memanggil dirinya-sendiri, hanya mengembalikan dirinya\n}\n```\n\n`f` ini akan digunakan didalam pemanggilan selanjutnya, dan lagi akan mengembalikan dirinya-sendiri, berapa kalipun seperti yang dibutuhkan. Lalu, ketika digunakan sebagai angka atau sebuah string -- `toString` mengembalikan `currentSum`. Kita jadi bisa menggunakan `Symbol.toPrimitive` atau `valueOf` disini sebagai perubahan.\n"
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/task.md",
    "content": "nilai penting: 2\n\n---\n\n# Tambahkan dengan jumlah kurung yang banyak\n\nBuatlah sebuah fungsi `sum` yang harus bekerja seperti ini:\n\n```js\nsum(1)(2) == 3; // 1 + 2\nsum(1)(2)(3) == 6; // 1 + 2 + 3\nsum(5)(-1)(2) == 6\nsum(6)(-1)(-2)(-3) == 0\nsum(0)(1)(2)(3)(4)(5) == 15\n```\n\nCatatan. Kamu mungkin perlu untuk mengatur objek kostum menjadi perngubah primitif didalam fungsi kamu."
  },
  {
    "path": "1-js/06-advanced-functions/06-function-object/article.md",
    "content": "\n# Objek fungsi, NFE\n\nSeperti yang telah kita tahu, sebuah fungsi didalam Javascript adalah sebuah nilai.\n\nSetiap nilai didalam Javascript memiliki sebuah tipe. Apa tipe dari sebuah fungsi?\n\nDidalam Javascript, fungsi adalah objek.\n\nSebuah cara yang bagus untuk membayangkan fungsi adalah sebagai \"aksi objek\" yang dapat dipanggil. Kita tidak hanya memanggil mereka, tapi juga memperlakukannya seperti sebuah objek: menambah/menghapus properti, memberikan referensi dll.\n\n\n## Properti \"name\"\n\nObjek fungsi mengandung beberapa properti yang dapat digunakan.\n\nContoh, sebuah nama fungsi dapat diakses sebagai properti \"name\":\n\n```js run\nfunction sayHi() {\n  alert(\"Hi\");\n}\n\nalert(sayHi.name); // sayHi\n```\n\nItu terlihat cukup lucu, logika untuk penggunaan nama cukup pintar. Itu juga menggunakan nama fungsi yang benar bahkan jika tidak terdapat nama sekalipun, dan jika langsung ditempatkan:\n\n```js run\nlet sayHi = function() {\n  alert(\"Hi\");\n};\n\nalert(sayHi.name); // sayHi (ada namanya!)\n```\n\nItu juga akan bekerja jika assignmentnya dilakukan dengan menggunakan nilai default:\n\n```js run\nfunction f(sayHi = function() {}) {\n  alert(sayHi.name); // sayHi (bekerja!)\n}\n\nf();\n```\n\nDidalam spesifikasinya, fitur ini dinamakan dengan \"nama kontekstual/contextual name\". Jika sebuah fungsi tidak memiliki nama, maka didalam assignment-nya akan mencarinya didalam konteksnya.\n\nMetode objek mempunyai nama juga:\n\n```js run\nlet user = {\n\n  sayHi() {\n    // ...\n  },\n\n  sayBye: function() {\n    // ...\n  }\n\n}\n\nalert(user.sayHi.name); // sayHi\nalert(user.sayBye.name); // sayBye\n```\n\nTidak ada sulap disini. Terdapat kasus dimana tidak ada cara untuk mengetahui namanya. Didalam kasus itu, properti namanya akan kosong, seperti disini:\n\n```js run\n// fungsi dibuat didalam array\nlet arr = [function() {}];\n\nalert( arr[0].name ); // <string kosong>\n// mesinnya tidak memiliki cara untuk mengetahui namanya, maka isinya akan menjadi kosong\n```\n\nDidalam penerapannya, entah bagaimana, kebanyakan fungsi akan memiliki nama.\n\n## Properti \"length\"\n\nJuga terdapat properti bawaan \"length\" yang mengembalikan jumlah dari parameter sebuah fungsi, contoh:\n\n```js run\nfunction f1(a) {}\nfunction f2(a, b) {}\nfunction many(a, b, ...more) {}\n\nalert(f1.length); // 1\nalert(f2.length); // 2\nalert(many.length); // 2\n```\n\nDisini kita bisa melihat parameter rest tidak dihitung.\n\nProperti `length` terkadang digunakan untuk [introspeksi](https://en.wikipedia.org/wiki/Type_introspection) didalam fungsi yang mengoperasikan fungsi lainnya.\n\nContoh, didalam kode dibawah fungsi `ask` menerima sebuah `question` untuk ditanyakan dan sebuah angka yang panjang dari fungsi `handler` untuk dipanggil.\n\nSekalinya pengguna memberikan jawaban mereka, pemanggilan fungsi akan memanggil handler-nya. Kita bisa memberikan dua macan handler:\n\n- Fungsi dengan jumlah argumen nol, yang mana hanya dipanggil ketika pengguna memberikan jawaban yang positif.\n- Fungsi dengan argumen, yang mana akan dipanggil diantara salah satu kasusnya dan mengembalikan sebuah jawaban.\n\nUntuk memanggil `handler` dengan cara yang tepat, kita memeriksa properti `handler.length`.\n\nIdenya adalah kita mempunyai handler yang simpel tanpa argumen untuk kasus yang positif (pada kasus yang sering terjadi), tapi yang tentunya tidak mendukung handler yang universal:\n\n```js run\nfunction ask(question, ...handlers) {\n  let isYes = confirm(question);\n\n  for(let handler of handlers) {\n    if (handler.length == 0) {\n      if (isYes) handler();\n    } else {\n      handler(isYes);\n    }\n  }\n\n}\n\n// untuk jawaban yang positif, kedua handler-nya akan dipanggil\n// untuk jawaban yang negatif, hanya yang kedua\nask(\"Question?\", () => alert('You said yes'), result => alert(result));\n```\n\nIni hanyalah kasus tertentu yang dipanggil dengan [polimorfisme](https://en.wikipedia.org/wiki/Polymorphism_(computer_science)) -- memperlakukan argumen berbeda-beda tergantung dari tipenya atau, didalam kasus kita tergantung dari `length`. Idenya adalah didalam penggunakan librari Javascript.\n\n## Properti-properti kustom/Custom properties\n\nKita juga bisa menambahkan properti-properti milik kita sendiri.\n\nDisini kita menambahkan properti `counter` untuk melacar total dari pemanggilan:\n\n```js run\nfunction sayHi() {\n  alert(\"Hi\");\n\n  *!*\n  // hitung berapa kali kita berjalan\n  sayHi.counter++;\n  */!*\n}\nsayHi.counter = 0; // nilai awal\n\nsayHi(); // Hi\nsayHi(); // Hi\n\nalert( `Called ${sayHi.counter} times` ); // dipanggil dua kali\n```\n\n```warn header=\"Sebuah properti bukanlah sebuah variabel\"\nSebuah properti dimasukan kedalam fungsi seperti `sayHi.counter = 0` tidak mendefinisikan variabel lokal `counter` didalamnya. Dengan kata lain, sebuah properti `counter` dan sebuah variabel `let counter` adalah dua hal yang tidak memiliki hubungam sama sekali.\n\nKita bisa memperlakukan sebuah fungsi seperti sebuah objek, menyimpan properti didalamnya, tapi itu tidak akan mempengaruhi eksekusinya sendiri. Variabel bukanlah sebuah properti fungsi dan sebaliknya. Keduanya hanyalah dua hal yang berbeda.\n```\n\nTerkadang properti fungsi bisa menggantikan closure. Contoh, kita bisa menulis ulang contoh fungsi counter dari bab <info:closure> untuk menggunakan properti fungsi:\n\n```js run\nfunction makeCounter() {\n  // daripada:\n  // let count = 0\n\n  function counter() {\n    return counter.count++;\n  };\n\n  counter.count = 0;\n\n  return counter;\n}\n\nlet counter = makeCounter();\nalert( counter() ); // 0\nalert( counter() ); // 1\n```\n\n`count`nya sekarang tersimpan didalam fungsinya langsung, bukan diluar dari lingkungan leksikalnya.\n\nApakah lebih baik atau tidak menggunakan closure?\n\nPerbedaan utamanya adalah jika nilai dari `count` berada didalam variabel luar, maka kode eksternal tidak dapat mengaksesnya. Hanya fungsi bercabang yang bisa memodifikasinya. Dan jika itu terikat dengan sebuah fungsi, maka kode eksternal dapat mengaksesnya.\n\n```js run\nfunction makeCounter() {\n\n  function counter() {\n    return counter.count++;\n  };\n\n  counter.count = 0;\n\n  return counter;\n}\n\nlet counter = makeCounter();\n\n*!*\ncounter.count = 10;\nalert( counter() ); // 10\n*/!*\n```\n\nJadi pilihannya implementasinya adalah tergantung pada kebutuhan kita.\n\n## Ekspresi fungsi yang mempunyai nama\n\nEkspresi fungsi yang mempunyai nama, atau NFE(Named Function Expression) adalah istilah untuk ekspresi fungsi yang memiliki nama.\n\nContoh, ayo kita lihat ekspresi fungsi yang biasa:\n\n```js\nlet sayHi = function(who) {\n  alert(`Hello, ${who}`);\n};\n```\n\nDan tambahkan nama:\n\n```js\nlet sayHi = function *!*func*/!*(who) {\n  alert(`Hello, ${who}`);\n};\n```\n\nApakah kita meraih sesuatu disini? Apa tujuan dari penambahan nama `\"func\"`?\n\npertama kita perhatikan, bahwa kita masih memiliki ekspresi fungsi. Menambahkan nama `\"func\"` setelah `function` tidaklah membuatnya menjadi deklarasi fungsi, karena itu masih dibuat sebagai bagian dari sebuah assignment ekspresi.\n\nMenambahkan nama seperti itu tidak akan merusak apapun.\n\nFungsinya masih ada sebagai `sayHi()`:\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  alert(`Hello, ${who}`);\n};\n\nsayHi(\"John\"); // Hello, John\n```\n\nTerdapat dua hal yang spesial tentang nama `func`, hal itu adalah:\n\n1. Itu mengijinkan fungsinya untuk mereferensi dirinya sendiri secara internal.\n2. Fungsinya tidak akan terlihat diluar fungsi tersebut.\n\nUntuk contoh, fungsi `sayHi` dibawah memanggil dirinya-sendiri lagi dengan `\"Guest\"` jika `who` tidak ada:\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    func(\"Guest\"); // gunakan func untuk memanggil dirinya sendiri\n*/!*\n  }\n};\n\nsayHi(); // Hello, Guest\n\n// Tapi hal ini tidak akan bekerja\nfunc(); // Error, func is not defined (tidak terliha diluar fungsinya sendiri)\n```\n\nKenapa kita menggunakan `func`? Mungkin cukup gunakan `sayHi` untuk pemanggilan bercabang?\n\n\nSebenarnya, dalam kebanyakan kasus kita bisa:\n\n```js\nlet sayHi = function(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    sayHi(\"Guest\");\n*/!*\n  }\n};\n```\n\nMasalahnya dengan kode itu adalah `sayHi` mungkin akan berubah di kode terluar. Malah jika fungsinya dimasukan kedalam variabel lain, kodenya akan mulai memberikan error:\n\n```js run\nlet sayHi = function(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    sayHi(\"Guest\"); // Error: sayHi is not a function\n*/!*\n  }\n};\n\nlet welcome = sayHi;\nsayHi = null;\n\nwelcome(); // Error, pemanggilan bercabang sayHi tidak akan bekerja lagi!\n```\n\nHal itu terjadi karena fungsinya menggunakan `sayHi` dari luar lingkungan leksikalnya. Disana tidak ada `sayHi` lokal, jadi variabel terluar digunakan. Dan pada saat pemanggilannya terjadi `sayHi` terluar adalah `null`.\n\nNama opsional dimana kita bisa memasukannya kedalam ekspresi fungsi diartikan untuk menyelesaikan masalah yang tepat seperti ini.\n\nAyo kita gunakan itu untuk membetulkan masalah pada kode kita:\n\n```js run\nlet sayHi = function *!*func*/!*(who) {\n  if (who) {\n    alert(`Hello, ${who}`);\n  } else {\n*!*\n    func(\"Guest\"); // Sekarang semuanya mantap\n*/!*\n  }\n};\n\nlet welcome = sayHi;\nsayHi = null;\n\nwelcome(); // Hello, Guest (pemanggilan bercabang bekerja)\n```\n\nSekarang hal itu bekerja karena nama `\"func\"` adalah fungsi-lokal. Fungsi itu tidak diambil dari luar (dan tidak terlihat dari luar). Spesifikasinya menjamin itu akan selalu mereferensi fungsi saat ini. \n\nFungsi dari luar kode mempunyai variabel `sayHi` atau `welcome`nya sendiri. Dan `func` adalah sebuah \"nama fungsi internal\", bagaimana fungsi bisa memanggil dirinya sendiri secara internal.\n\n```smart header=\"Tidak ada hal semacam itu untuk deklarasi fungsi\"\nFitur \"nama internal\" dideskripsikan disini hanya tersedia untuk ekspresi fungsi, bukan deklarasi fungsi. Untuk deklarasi fungsi, tidak terdapat sintaks untuk menambahkan sebuah nama \"internal\".\n\nTerkadang, ketika kita membutuhkan nama internal yang dapat diandalkan, itulah alasan yang tepat untuk menulis ulang sebuah deklarasi fungsi kedalam bentuk ekspresi fungsi.\n```\n\n## Ringkasan\n\nFungsi adalah objek.\n\nDisini kita memperlajari properti-propertinya:\n\n- `name` -- nama dari fungsinya. Biasanya diambil dari definisi fungsinya, tapi jika disana tidak ada, Javascript akan mencoba mencarinya dari konteksnya (contoh. dari assignment-nya).\n- `length` -- jumlah dari argumen didalam definisi dari fungsi. Parameter rest tidak dihitung.\n\nJika fungsinya di deklarasikan sebagai ekspresi fungsi (tidak didalam alur kode utama), dan itu memiliki nama, maka itu dipanggil dengan ekspresi fungsi yang memiliki nama. Namanya bisa digunakan didalam fungsinya untuk mereferensi dirinya sendiri, untuk pemanggilan rekursif atau sejenisnya.\n\nJuga, fungsi mungkin memiliki properti tambahan. Beberapa librari Javascript yang cukup terkenal banyak menggunakan fitur ini.\n\nMereka membuat sebuah fungsi \"utama\" dan mengkaitkannya dengan fungsi \"pembantu\". Contoh librari [jQuery](https://jquery.com) menciptakan fungsi bernama `$`. Librari The [lodash](https://lodash.com) membuat sebuah fungsi `_` dan lalu menambahkan `_.clone`, `_.keyBy`dan properti lainnya kedalamnya (lihat [dokumentasinya](https://lodash.com/docs) ketika kamu mau tau lebih dalam). Sebenarnya, mereka melakukannya untuk mengurangi penggunaan dari ruang global, jadi librari tunggal itu hanya menggunakan satu variabel global. Itu mengurangi kemungkinan dari konflik penamaan variabel.\n\n\nJadi, sebuah fungsi bisa melakukan hal-hal yang berguna dengan dirinya-sendiri dan juga membawa setumpuk fungsionalitas didalam propertinya sendiri."
  },
  {
    "path": "1-js/06-advanced-functions/07-new-function/article.md",
    "content": "\n# Sintaks \"new Function\"\n\nTerdapat satu lagi cara untuk membuat fungsi. Ini sangat jarang digunakan, tapi terkadang tidak ada alternatif lain.\n\n## Sintaks\n\nSintaks untuk membuat sebuah fungsi:\n\n```js\nlet func = new Function ([arg1, arg2, ...argN], functionBody);\n```\n\nFungsinya dibuat dengan argumen-argumen `arg1...argN` dan diberikan `functionBody`.\n\nIni sangat mudah dimengerti hanya dengan melihat contohnya. Disini sebuah fungsi dengan dua argumen:\n\n```js run\nlet sum = new Function('a', 'b', 'return a + b');\n\nalert( sum(1, 2) ); // 3\n```\n\nDan ini fungsi tanpa argumen, hanya ada body dari fungsinya:\n\n```js run\nlet sayHi = new Function('alert(\"Hello\")');\n\nsayHi(); // Hello\n```\n\nPerbedaan utama dari cara lain yang pernah kita lihat adalah fungsinya dibuat secara harfiah dari sebuah string, yang dilanjutkan ke run-time.\n\nSeluruh deklarasi sebelumnya membutuhkan kita, programmer, untuk menulis kode fungsi didalam skrip.\n\nTapi `new Function` mengijinkan kita untuk mengubah string apapun menjadi fungsi. Contoh, kita bisa menerima sebuah fungsi baru dari server dan mengeksekusinya:\n\n```js\nlet str = ... receive the code from a server dynamically ...\n\nlet func = new Function(str);\nfunc();\n```\n\nItu digunakan dalam beberapa kasus yang spesifik, seperti ketika kita menerima kode dari server, atau secara dinamis mengkompilasikan sebuah fungsi dari sebuah template didalam aplikasi-web yang kompleks.\n\n## Closure\n\nBiasanya, sebuah fungsi mengingat dimana dirinya dibuat didalam properti spesial `[[Environtment]]. Fungsi itu akan mereferensi lingkungan leksikal dari dimana ia dibuat (kita telah membahasnya didalam bab <info:closure>).\n\nTapi ketika sebuah fungsi dibuat menggunakan `new Function`, `[[Environment]]` miliknya disetel bukan pada lingkungan leksikal saat ini, tapi pada yang global.\n\nJadi, fungsi seperti itu tidak memiliki akses kepada variabel luar, hanya pada yang global saja.\n\n```js run\nfunction getFunc() {\n  let value = \"test\";\n\n*!*\n  let func = new Function('alert(value)');\n*/!*\n\n  return func;\n}\n\ngetFunc()(); // error: value is not defined\n```\n\nBandingkan dengan yang biasa:\n\n```js run\nfunction getFunc() {\n  let value = \"test\";\n\n*!*\n  let func = function() { alert(value); };\n*/!*\n\n  return func;\n}\n\ngetFunc()(); // *!*\"test\"*/!*, berasal dari lingkungan leksikal dari getFung\n```\n\nFitur spesial dari `new Function` ini terlihat aneh, tapi akan sangat berguna didalam penerapannya.\n\nBayangkan kalau kita harus membuat sebuah fungsi dari string. Kode dari fungsinya tidak diketahui saat penulisan skrip (itulah kenapa kita tidak menggunakan fungsi yang biasa), tapi akan diketahui saat proses dari eksekusinya. Kita mungkin menerima kodenya itu dari server atau sumber lainnya.\n\nFungsi baru kita membutuhkan interaksi dengan skrip utamanya.\n\nBagaimana jika itu bisa mengakses variabel luar?\n\nMasalahnya adalah saat Javascript belum dipublikasikan untuk produksi, itu akan dikompresi menggunakan *minifier* -- sebuah program spesial yang mengecilkan ukuran kode dengan menghapus komentar-komentar, spasi dan -- yang paling penting, menamai variabel lokal menjadi lebih pendek.\n\nContoh, jika sebuah fungsi mempunyai `let userName`, minifier akan mengganti itu dengan `let a` (atau huruf lainnya jika tidak hurufnya tidak tersedia), dan melakukannya dimanapun. Sebenarnya itu adalah yang yang aman untuk dilakukan, karena variabelnya lokal, tidak ada sesuatu dari luar fungsinya yang bisa mengaksesnya. Dan didalam fungsinya, minifier mengganti seluruh penamaan variabelnya. Minifier cukup pintar, mereka menganalisa struktur kodenya, jadi mereka tidak akan merusak apapun. Minifier bukanlah hal bodoh yang hanya akan mencari-dan-mengganti.\n\nJadi jika `new Function` mempunyai akses ke variabel luar, itu tidak akan bisa menemukan `userName` yang telah dinamai ulang.\n\n**Jika `new Function` mempunyai akses ke variabel luar, itu akan membuat masalah dengan minifiernya..**\n\nSelain itu, kode seperti itu secara arsitekturnya jelek dan rentan terhadap error.\n\nUntuk memberikan sesuatu kepada fungsi, dibuat sebagai `new Function`, kita seharusnya menggunakan argumennya.\n\n## Ringkasan\n\nSintaks:\n\n```js\nlet func = new Function ([arg1, arg2, ...argN], functionBody);\n```\n\nUntuk sebuah alasan lama, argumen-argumen bisa diberikan sebagai daftar dengan koma.\n\nKetiga deklarasi ini melakukan hal yang sama:\n\n```js\nnew Function('a', 'b', 'return a + b'); // sintaks dasar\nnew Function('a,b', 'return a + b'); // dipisahkan dengan kona\nnew Function('a , b', 'return a + b'); // dipisahkan dengan koma dan ditambah spasi\n```\n\nFungsi yang dibuat dengan `new Function`, memiliki `[[Environment]]` mereferensi kepada lingkungan leksikal global, bukan bagian luarnya. Karenanya, mereka tidak bisa menggunakan variabel di bagian luarnya. Tapi sebenarnya itu bagus karena itu memastikan kita menjauh dari error. Memberikan parameter secara jelas adalah metode yang lebih baik secara arsitektur dan tidak akan menyebabkan error pada minifier.\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/1-output-numbers-100ms/solution.md",
    "content": "\nUsing `setInterval`:\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  let timerId = setInterval(function() {\n    alert(current);\n    if (current == to) {\n      clearInterval(timerId);\n    }\n    current++;\n  }, 1000);\n}\n\n// penggunaan:\nprintNumbers(5, 10);\n```\n\nUsing nested `setTimeout`:\n\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  setTimeout(function go() {\n    alert(current);\n    if (current < to) {\n      setTimeout(go, 1000);\n    }\n    current++;\n  }, 1000);\n}\n\n// penggunaan:\nprintNumbers(5, 10);\n```\n\nPerhatikan dikedua solusinya, disana terdapat penundaan awal sebelum keluaran pertamanya. Fungsinya dipanggil setelah `1000ms` saat pertama kali.\n\nJika kita juga ingin fungsinya untuk berjalan langsung, maka kita bisa menambahkan pemanggilan di baris yang berbeda, seperti ini:\n\n```js run\nfunction printNumbers(from, to) {\n  let current = from;\n\n  function go() {\n    alert(current);\n    if (current == to) {\n      clearInterval(timerId);\n    }\n    current++;\n  }\n\n*!*\n  go();\n*/!*\n  let timerId = setInterval(go, 1000);\n}\n\nprintNumbers(5, 10);\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/1-output-numbers-100ms/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Membuat keluaran setiap detik\n\nTulis sebuah fungsi `printNumbers(from, to)` yang mengeluarkan angka setiap detik, dimulai dari `from` dan berakhir sampai `to`.\n\nBuatlah dua varian solusinya.\n\n1. Menggunakan `setInterval`.\n2. Gunakan `setTimeout` bercabang.\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/4-settimeout-result/solution.md",
    "content": "\n`setTimeout` apapun akan berjalan hanya setelah kode yang sedang berjalan saat ini telah selesai.\n\n`i`nya akan menjadi yang terakhir: `100000000`.\n\n```js run\nlet i = 0;\n\nsetTimeout(() => alert(i), 100); // 100000000\n\n// asumsikan waktu untuk mengeksekusi fungsi ini lebih dari 100ms\nfor(let j = 0; j < 100000000; j++) {\n  i++; \n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/4-settimeout-result/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Apa yang akan ditampilkan setTimeout?\n\nDi kode dibawah disana terdapat pemanggilan `setTimeout` yang sudah terjadwal, lalu kalkulasi yang cukup berat berjalan, yang memakan waktu lebih dari 100ms untuk selesai.\n\nKapankan fungsi yang sudah dijadwal akan berjalan?\n\n1. Setelah perulangannya.\n2. Sebelum perulangannya.\n3. Di awal dari perulangannya.\n\nApakan yang akan `alert` tampilkan?\n\n```js\nlet i = 0;\n\nsetTimeout(() => alert(i), 100); // ?\n\n// asumsikan waktu untuk mengeksekusi fungsi ini lebih dari 100ms\nfor(let j = 0; j < 100000000; j++) {\n  i++; \n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/08-settimeout-setinterval/article.md",
    "content": "# Pendadwalan: setTimeout dan setInterval\n\nKita mungkin memutuskan untuk mengeksekusi fungsinya bukanlah sekarang, tapi pada waktu-waktu tertentu nanti. Itu dipanggil dengan \"penjadwalan pemanggilan\".\n\nTerdapat dua metode untuk itu:\n\n- `setTimeout` mengijinkan kita untuk menjalankan fungsinya setelah interval dari waktu.\n- `setInterval` mengijinkan kita untuk menjalankan fungsinya berulang-ulang, dimulai seterlah interval dari waktu yang diberikan, lalu akan terus berulang pada interval waktu yang diberikan.\n\nMetode-metode ini bukanlah bagian dari spesifikasi Javascript. Tapi kebanyakan lingkungan memiliki penjadwalan internal dan menyediakan metode-metode ini. Khususnya, mereka didukung didalam semua peramban dan Node.js.\n\n## setTimeout\n\nSintaksnya:\n\n```js\nlet timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)\n```\n\nParameters:\n\n`func|code`\n: Fungsi atau sebuah string dari kode untuk dieksekusi.\nBiasanya, adalah sebuah fungsi. Untuk beberapa alasan, sebuah string dari kode bisa diberikan, tapi hal itu tidak direkomendasikan.\n\n`delay`\n: Penundaannya sebelum berjalan, didalam milidetik (1000 milidetik = 1 detik), secara default 0.\n\n`arg1`, `arg2`...\n: Argumen-argumen untuk fungsinya (tidak didukung didalam IE9-).\n\nContoh, kode ini memanggil `sayHi()` setelah satu detik:\n\n```js run\nfunction sayHi() {\n  alert('Hello');\n}\n\n*!*\nsetTimeout(sayHi, 1000);\n*/!*\n```\n\nDengan argumen:\n\n```js run\nfunction sayHi(phrase, who) {\n  alert( phrase + ', ' + who );\n}\n\n*!*\nsetTimeout(sayHi, 1000, \"Hello\", \"John\"); // Hello, John\n*/!*\n```\n\nJika argumen pertama adalah sebuah string, maka Javascript akan membuatkan fungsi untuk itu.\n\nJadi, seperti ini juga akan bekerja:\n\n```js run no-beautify\nsetTimeout(\"alert('Hello')\", 1000);\n```\n\nTapi menggunakan string tidak direkomendasikan, lebih baik gunakan fungsi arrow, seperti ini:\n\n```js run no-beautify\nsetTimeout(() => alert('Hello'), 1000);\n```\n\n````smart header=\"Berikan sebuah fungsi, tapi jangan dijalankan\"\nDeveloper pemula terkadang membuat sebuah kesalahan dengan menambahkan kurung `()` setelah fungsinya:\n\n```js\n// wrong!\nsetTimeout(sayHi(), 1000);\n```\nItu tidak akan bekerja, karena `setTimeout` mengharapkan sebuah referensi kepada fungsi. Dan disini `sayHi()` akan menjalankan fungsinya, dan *hasil dari eksekusinya* akan diberikan kepada `setTimeout`. Didalam kasus kita hasil dari `sayHi()` adalah `undefined` (fungsinya tidak mengembalikan apapun), jadi tidak ada hal yang dijadwalkan.\n````\n\n### Pembatalan menggunakan clearTimeout\n\nPemanggilan `setTimeout` mengembalikan sebuah \"identifier waktu\" `timerId` yang bisa kita gunakan untuk membatalkan eksekusinya.\n\nSintaks untuk membatalkan:\n\n```js\nlet timerId = setTimeout(...);\nclearTimeout(timerId);\n```\n\nDi kode dibawah, kita menjadwalkan fungsinya dan membatalkannya (berubah pikiran). Sebagai hasilnya, tidak ada yang terjadi:\n\n```js run no-beautify\nlet timerId = setTimeout(() => alert(\"never happens\"), 1000);\nalert(timerId); // identifier waktu\n\nclearTimeout(timerId);\nalert(timerId); // identifier yang sama (tidak akan menjadi null setelah dibatalkan)\n```\n\nSeperti yang bisa kita lihat dari keluaran `alert`, didalam peramban identifier timernya adalah sebuah angka. Didalam lingkungan pengembangan lainnya, identifiernya bisa saja sesuatu yang lain. Contoh, Node.js mengembalikan objek timer dengan metode tambahan.\n\nLainnya, tidak terdapat spesifikasi universal untuk metode-metode ini, jadi tidak ada masalah.\n\nUntuk peramban, timer dideskripsikan didalam [bagian timer](https://www.w3.org/TR/html5/webappapis.html#timers) dari standar HTML5.\n\n## setInterval\n\nMetode `setInterval` mempunyai sintaks yang sama seperti `setTimeout`:\n\n```js\nlet timerId = setInterval(func|code, [delay], [arg1], [arg2], ...)\n```\n\nSeluruh argumennya mempunyai arti yang sama. Tapi tidak seperti `setTimeout` fungsinya akan berjalan tidak sekali, tapi akan berjalan secara teratur dengan interval waktu yang diberikan.\n\nUntuk menghentikan pemanggilan selanjutnya, kita harus memanggil `clearInterval(timerId)`.\n\nContoh berikut akan memperlihatkan pesan setiap 2 detik. Setelah 5 detik, keluarannya akan dihentikan:\n\n```js run\n// Ulangi dengan interval 2 detik\nlet timerId = setInterval(() => alert('tick'), 2000);\n\n// setelah 5 detik berhenti\nsetTimeout(() => { clearInterval(timerId); alert('stop'); }, 5000);\n```\n\n```smart header=\"Waktu terus berjalan sementara `alert` ditampilkan\"\nDi kebanyakan peramban, termasuk Chrome dan Firefox penghitung waktu internal berlanjut \"berdetik\" selagi menampilkan `alert/confirm/prompt`.\n\nJadi jika kamu menjalankan kode diatas dan tidak menyingkirkan jendela `alert` untuk beberapa saat, maka `alert` selanjutnya akan langsung muncul. Interval sebenarnya diantara alert lebih pendek dari 2 detik.\n```\n\n## setTimeout bercabang\n\nTerdapat dua cara untuk menjalankan sesuatu secara terus-menerus.\n\nSatu adalah `setInterval`. Dan satunya lagi adalah `setTimeout` bercabang, seperti ini:\n\n```js\n/** daripada menggunakan:\nlet timerId = setInterval(() => alert('tick'), 2000);\n*/\n\nlet timerId = setTimeout(function tick() {\n  alert('tick');\n*!*\n  timerId = setTimeout(tick, 2000); // (*)\n*/!*\n}, 2000);\n```\n\n`setTimeout` diatas menjadwalkan pemanggilan selanjutnya tepat setelah akhir `(*)`.\n\n`setTimeout` bercabang metode yang lebih fleksibel daripada `setInterval`. Dengan cara ini pemanggilan mungkun dapat dijadwalkan dengan interval yang berbeda, tergantung dari hasil sebelumnya.\n\nContoh, kita perlu menulis sebuah service yang mengirim sebuah request kepada server setiap 5 detik untuk menanyakan data, tapi di kasus ini servernya sedang sibuk, maka intervalnya harus dinaikan menjadi 10, 20, 40 detik...\n\nIni adalah pseukodenya:\n```js\nlet delay = 5000;\n\nlet timerId = setTimeout(function request() {\n  ...send request...\n\n  if (request failed due to server overload) {\n    // interval dinaikan untuk pemanggilan selanjutnya\n    delay *= 2;\n  }\n\n  timerId = setTimeout(request, delay);\n\n}, delay);\n```\n\n\nDan jika fungsi yang sudah kita jadwalkan ternyata membutuhkan sumber-daya yang besar, maka kita bisa mengukur waktu yang diabmbil oleh eksekusinya dan mengatur ulang eksekusi selanjutnya agar lebih cepat atau lebih lambat.\n\n**`setTimeout` membolehkan untuk menyetel penundaan eksekusi-eksekusinya lebih presisi daripada `setInterval`.**\n\nAyo kita bandingkan dua pecahan kode berikut. Yang pertama menggunakan `setInterval`:\n\n```js\nlet i = 1;\nsetInterval(function() {\n  func(i++);\n}, 100);\n```\n\nYang kedua menggunakan `setTimeout` bercabang:\n\n```js\nlet i = 1;\nsetTimeout(function run() {\n  func(i++);\n  setTimeout(run, 100);\n}, 100);\n```\n\nUntuk `setInterval` penjadwal internalnya akan berjalan `func(i++)` setiap 100ms:\n\n![](setinterval-interval.svg)\n\nApakah kamu memperhatikannya?\n\n**penundaan sebenarnya diantara pemanggilan `func` pada `setInterval` lebih cepat daripada apa yang ada pada kodenya!**\n\nItu adalah hal yang normal, karena waktu yang diambil oleh eksekusi `func` \"mengambil\" bagian dari intervalnya.\n\nAdalah hal yang mungkin jika eksekusi `func` ternyata lebih lama daripada yang kita harapkan dan memakan lebih dari 100ms.\n\nDidalam kasus ini mesinnya menunggu `func` untuk selesai, lalu memeriksa penjadwalnya dan jika waktunya sudah berakhir, maka akan *langsung* dieksekusi lagi.\n\nDidalam kasus yang jarang, jika fungsinya selalu mengeksekusi lebih lama daripada `penundaan` ms, maka pemanggilannya akan terjadi tanpa berhenti sama sekali.\n\nDan ini adalah gambaran dari `setTimeoute` bercabang:\n\n![](settimeout-interval.svg)\n\n**`setTimeout` bercabang menjamin penundaan yang tepar (disini 100ms).**\n\nItu karena pemanggilan baru sudah direncanakan pada akhir dari pemanggilan sebelumnya.\n\n````smart header=\"Garbage collection and callback pada setInterval/setTimeout\"\nKetika sebuah fungsi dimasukan kedalam `setInterval/setTimeout`, sebuah referensi interval dibuat kedalamnya dan disimpan didalam penjadwal. Itu akan mencegah fungsinya dari pembuangan (dihilangkan dari memori), bahkan jika disana sudah tidak ada yang mereferensi kedalam fungsinya lagi.\n\n```js\n// fungsinya tetap berada di memori sampai penjadwalnya memanggil lagi\nsetTimeout(function() {...}, 100);\n```\n\nUntuk `setInterval` fungsinya akan tetap didalam memori sampai `clearInterval` dipanggil.\n\nTidak terdapat efek-samping pada hal itu. Sebuah fungsi mereferensi lingkungan leksikal luar, jadi, selama itu masih ada, variabel luar pun akan tetap ada. Hal itu mungkin akan memakan memori daripada fungsinya sendiri. Jadi ketika kita tidak butuh fungsi yang sudah dijadwalkan lagi, akan lebih baik untuk dibatalkan/diberhentikan, bahkan jika itu sebuah kode yang sangat pendek/kecil.\n````\n\n## setTimeout dengan penundaan nol\n\nTerdapat sebuah kasus spesial: `setTimeout(func, 0)`, atau hanya `setTimeout(func)`.\n\nPenjadwalan eksekusi dari `func` akan dilakukan secepat mungkin. Tapi penjadwal akan memanggilnya hanya setelah skrip yang sedang berjalan selesai dieksekusi.\n\nJadi fungsinya dijadwalkan untuk berjalan \"tepat setelah\" skrip yang sedang berjalan.\n\nContoh, dibawah akan mengeluarkan \"Hello\", lalu langsung \"World\":\n\n```js run\nsetTimeout(() => alert(\"World\"));\n\nalert(\"Hello\");\n```\n\nPada baris pertama \"akan memasukan pemanggilan kedalam urutan pemanggilan setelah 0ms\". Tapi penjadwal hanya akan \"memeriksa urutanya\" setelah skrip yang sedang berjalan selesai, jadi `\"Hello\"` adalah pertama, dan `\"World\"` -- setelahnya.\n\nJuga terdapat kasus yang berhubungan dengan peramban, kita akan membahasnya didalam bab <info:event-loop>.\n\n````smart header=\"Penundaan dengan nol faktanya tidaklah nol (didalam peramban)\"\nDidalam peramban, terdapat sebuah batasan seberapa seringnya timer bercabang bisa berjalan. [standar HTML5](https://html.spec.whatwg.org/multipage/timers-and-user-prompts.html#timers) mengatakan: \"setelah lima timer bercabang, intervalnya dipaksa untuk berjalan setidaknya 4 milidetik.\".\n\nLet's demonstrate what it means with the example below. The `setTimeout` call in it re-schedules itself with zero delay. Each call remembers the real time from the previous one in the `times` array. What do the real delays look like? Let's see:\nAyo kita prakterkan apa artinya itu dengan contoh dibawah. Pemanggilan `setTimeout` menjadwalkan ulang dengan penundaan 0. Setiap pemanggilan mengingat waktu yang asli dari pemanggilan sebelumnya didalam array `times`. Seperti apa penundaan sesungguhnya terlihat? Lihat dibawah:\n\n```js run\nlet start = Date.now();\nlet times = [];\n\nsetTimeout(function run() {\n  times.push(Date.now() - start); // mengingat penundaan dari pemanggilan sebelumnya\n\n  if (start + 100 < Date.now()) alert(times); // tampilkan penundaanya setelah 100ms\n  else setTimeout(run); // atau akan dijadwalkan ulang\n});\n\n// contoh dari keluarannya:\n// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100\n```\n\nPertama timer akan berjalan secara langsung (seperti yang tertulis dalam spesifikasinya), dan lalu kita melihat `9, 15, 20, 24...`. Penundaan wajib 4+ms diantara pemanggilan akan berjalan.\n\nHal yang sama akan terjadi jika kita menggunakan `setInterval` daripada `setTimeout`: `setInterval(f)` menjalankan `f` beberapa kali dengan tanpa delay, dan setelahnya dengan 4+ms delay.\n\nBatasan itu sudah ada sejak lama dan beberapa skrip mengandalkan hal itu, jadi itu ada untuk beberapa alasan.\n\nUntuk Javascript dibagian server, batasan itu tidaklah ada, dan disana terdapat cara lain untuk menjadwalkan sebuah pekerjaan yang asinkronus, seperti [setImmediate](https://nodejs.org/api/timers.html) untuk Node.js. Jadi hal ini merupakan hal yang berada pada peramban.\n````\n\n## Ringkasan\n\n- Metode `setTimeout(func,delay, ...args)` dan `setInterval(func, delay, ...args)` mengijinkan kita untuk menjalankan fungsinya sekali atau terus menerus setelah `delay` milidetik.\n- Untuk membatalkan eksekusinya, kita harus memanggil `clearTimeout/clearInterval` dengan nilai yang dikembalikan oleh `setTimeout/setInterval`.\n- Pemanggilan `setTimeout` bercabang adalah alternatif yang lebih fleksibel dari `setInterval`, mengijinkan kita untuk menyetel waktu *diantara* eksekusinya dengan lebih presisi.\n- Penjadwalan dengan delay 0 dengan `setTimeout(func, 0)` (sama seperti `setTimeout(func)`) digunakan untuk menjadwalkan pemanggilan \"secepat mungkin, tapi setelah skrip yang sedang berjalan selesai\".\n- Peramban membatasi delay dengan minimal 5 atau lebih pada pemanggilan bercabang dari `setTimeout` atau dari `setInterval` (setelah pemanggilan kelima) menjadi 4ms. Hanyalah untuk alasan-alasan yang sudah lama.\n\nPerhatikan bahwa seluruh metode penjadwalan tidak *menjamin* delay yang tepat.\n\nContoh, didalam peramban timer mungkin lebih lambat untuk beberapa alasan:\n- CPU-nya sedang melakukan banyak pekerjaan.\n- Ada tab peramban yang sedang berjalan dalam mode background.\n- Laptopnya sedang menggunakan mode batre.\n\nSemua itu mungkin menaikan resolusi timernya (delay minimalnya) menjadi 300ms atau bahkan 1000ms tergantung perambannya dan performasi pada OS-nya.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/solution.js",
    "content": "function spy(func) {\n\n  function wrapper(...args) {\n    // gunakan ...args daripada argumen untuk menyimpan \"array asli\" didalam wrapper.calls\n    wrapper.calls.push(args);\n    return func.apply(this, args);\n  }\n\n  wrapper.calls = [];\n\n  return wrapper;\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/source.js",
    "content": "function spy(func) {\n  // Tulis kodemu disini\n}\n\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/_js.view/test.js",
    "content": "describe(\"spy\", function() {\n  it(\"records calls into its property\", function() {\n    function work() {}\n\n    work = spy(work);\n    assert.deepEqual(work.calls, []);\n\n    work(1, 2);\n    assert.deepEqual(work.calls, [\n      [1, 2]\n    ]);\n\n    work(3, 4);\n    assert.deepEqual(work.calls, [\n      [1, 2],\n      [3, 4]\n    ]);\n  });\n\n  it(\"transparently wraps functions\", function() {\n\n    let sum = sinon.spy((a, b) => a + b);\n\n    let wrappedSum = spy(sum);\n\n    assert.equal(wrappedSum(1, 2), 3);\n    assert(sum.calledWith(1, 2));\n  });\n\n\n  it(\"transparently wraps methods\", function() {\n\n    let calc = {\n      sum: sinon.spy((a, b) => a + b)\n    };\n\n    calc.wrappedSum = spy(calc.sum);\n\n    assert.equal(calc.wrappedSum(1, 2), 3);\n    assert(calc.sum.calledWith(1, 2));\n    assert(calc.sum.calledOn(calc));\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/solution.md",
    "content": "Pembungkus yang dikembalikan oleh `spy(f)` harus menyimpan semua argumen dan lalu menggunakan `f.apply` untuk melanjutkan pemanggilannya.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/01-spy-decorator/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Spy decorator\n\nBuatlah sebuah dekorator `spy(func)` yang harus mengembalikan pembungkus yang menyimpan semua pemanggilan kepada fungsinya didalam propertinya sendiri bernama `calls`.\n\nSetiap pemanggilan disimpan sebagai sebuah array dari argumen.\n\nContoh:\n\n```js\nfunction work(a, b) {\n  alert( a + b ); // bayangkan work adalah sebuah fungsi yang panjang\n}\n\n*!*\nwork = spy(work);\n*/!*\n\nwork(1, 2); // 3\nwork(4, 5); // 9\n\nfor (let args of work.calls) {\n  alert( 'call:' + args.join() ); // \"call:1,2\", \"call:4,5\"\n}\n```\n\nCatatan. Dekoratornya harus berguna untuk unit-testing. Bentuk lanjutannya adalah `sinon.spy` didalam librari [Sinon.JS](http://sinonjs.org/).\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/_js.view/solution.js",
    "content": "function delay(f, ms) {\n\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n\n};"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/_js.view/test.js",
    "content": "describe(\"delay\", function() {\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n  it(\"calls the function after the specified timeout\", function() {\n    let start = Date.now();\n\n    function f(x) {\n      assert.equal(Date.now() - start, 1000);\n    }\n    f = sinon.spy(f);\n\n    let f1000 = delay(f, 1000);\n    f1000(\"test\");\n    this.clock.tick(2000);\n    assert(f.calledOnce, 'calledOnce check fails');\n  });\n\n  it(\"passes arguments and this\", function() {\n    let start = Date.now();\n    let user = {\n      sayHi: function(phrase, who) {\n        assert.equal(this, user);\n        assert.equal(phrase, \"Hello\");\n        assert.equal(who, \"John\");\n        assert.equal(Date.now() - start, 1500);\n      }\n    };\n\n    user.sayHi = sinon.spy(user.sayHi);\n\n    let spy = user.sayHi;\n    user.sayHi = delay(user.sayHi, 1500);\n\n    user.sayHi(\"Hello\", \"John\");\n\n    this.clock.tick(2000);\n\n    assert(spy.calledOnce, 'calledOnce check failed');\n  });\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/solution.md",
    "content": "Solusi:\n\n```js run demo\nfunction delay(f, ms) {\n\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n\n}\n\nlet f1000 = delay(alert, 1000);\n\nf1000(\"test\"); // tampilkan \"test\" setelah 1000ms\n```\n\nPerhatikan bagaimana fungsi arrow digunakan disini. Seperti yang kita tahu, fungsi panah tidak memiliki `this` dan `argumen`nya sendiri, jadi `f.apply(this, arguments)` akan mengambil `this` dan `arguments` dari pembungkusnya.\n\nJika kita memasukan fungsi yang biasa, `setTimeout` akan memanggil fungsinya tanpa argumen dan `this=window` (asumsikan kita berada didalam peramban).\n\nKita masih bisa memberikan `this` yang benar dengan menggunakan variabel tambahan, tapi kodenya akan sedikit menjadi lebih rumit:\n\n```js\nfunction delay(f, ms) {\n\n  return function(...args) {\n    let savedThis = this; // simpan this kedalam variabel tambahan\n    setTimeout(function() {\n      f.apply(savedThis, args); // gunakan disini\n    }, ms);\n  };\n\n}\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/02-delay/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Dekorator penunda\n\nBuatlah sebuah dekorator `delay(f, ms)` yang menunda setiap pemanggilan dari `f` selama `ms` milidetik.\n\nContoh:\n\n```js\nfunction f(x) {\n  alert(x);\n}\n\n// create wrappers\nlet f1000 = delay(f, 1000);\nlet f1500 = delay(f, 1500);\n\nf1000(\"test\"); // tampilkan \"test\" setelah 1000ms\nf1500(\"test\"); // tampilkan \"test\" setelah 1500ms\n```\n\nDengan kata lain, `delay(f, ms)` mengembalikan sebuah \"varian dari `f` yang telah ditunda selama `ms`\".\n\nDidalam kode diatas, `f` adalah sebuah fungsi dari sebuah argumen tunggal, tapi solusimu harus bisa melewati seluruh argumen dan konteks dari `this`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/_js.view/solution.js",
    "content": "function debounce(func, ms) {\n  let timeout;\n  return function() {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => func.apply(this, arguments), ms);\n  };\n}\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/_js.view/test.js",
    "content": "describe('debounce', function () {\n  before(function () {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function () {\n    this.clock.restore();\n  });\n\n  it('for one call - runs it after given ms', function () {\n    const f = sinon.spy();\n    const debounced = debounce(f, 1000);\n\n    debounced('test');\n    assert(f.notCalled, 'not called immediately');\n    this.clock.tick(1000);\n    assert(f.calledOnceWith('test'), 'called after 1000ms');\n  });\n\n  it('for 3 calls - runs the last one after given ms', function () {\n    const f = sinon.spy();\n    const debounced = debounce(f, 1000);\n\n    debounced('a');\n    setTimeout(() => debounced('b'), 200); // diabaikan (terlalu dini)\n    setTimeout(() => debounced('c'), 500); // dijalankan (1000 ms telah berlalu)\n    this.clock.tick(1000);\n\n    assert(f.notCalled, 'not called after 1000ms');\n\n    this.clock.tick(500);\n\n    assert(f.calledOnceWith('c'), 'called after 1500ms');\n  });\n\n  it('keeps the context of the call', function () {\n    let obj = {\n      f() {\n        assert.equal(this, obj);\n      },\n    };\n\n    obj.f = debounce(obj.f, 1000);\n    obj.f('test');\n    this.clock.tick(5000);\n  });\n  \n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/debounce.view/index.html",
    "content": "<!doctype html>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\"></script>\n\nFunction <code>handler</code> is called on this input:\n<br>\n<input id=\"input1\" placeholder=\"type here\">\n\n<p>\n\nDebounced function <code>debounce(handler, 1000)</code> is called on this input:\n<br>\n<input id=\"input2\" placeholder=\"type here\">\n\n<p>\n<button id=\"result\">The <code>handler</code> puts the result here</button>\n\n<script>\n  function handler(event) {\n    result.innerHTML = event.target.value;\n  }\n\n  input1.oninput = handler;\n  input2.oninput = _.debounce(handler, 1000);\n</script>"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/solution.md",
    "content": "```js demo\nfunction debounce(func, ms) {\n  let timeout;\n  return function() {\n    clearTimeout(timeout);\n    timeout = setTimeout(() => func.apply(this, arguments), ms);\n  };\n}\n\n```\n\nPemanggilan kepada `debounce` mengembalikan sebuah pembungkus. Ketika dipanggil, `debounce` akan menunggu lalu memanggil fungsi aslinya setelah `ms` milidetik dan membatal kan timeout sebelumnya.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/03-debounce/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Debounce decorator\n\nHasil dari dekorator `debounce(f, ms)` adalah sebuah pembungkus yang menghentikan pemanggilan `f` selama `ms` milidetik dari ketidakaktifan (tidak ada pemanggilan, \"masa menunggu\"), lalu memanggil `f` sekali dengan argumen terakhir.\n\nDengan kata lain, `debounce` seperti seorang sekertaris yang menerima \"telefon\", dan menunggu selama `ms` milidetik dari ketidakaktifan. Dan lalu menyampaikan pemanggilan terakhir kepada \"boss\" (melakukan pemanggilan `f`).\n\nContoh, jika kita mempunyai sebuah fungsi `f` dan lalu memasukan `f = debounce(f, 1000)`.\n\nMaka jika fungsi pembungkus dipanggil pada 0ms, 200ms, dan 500ms, dan lalu tidak ada pemanggilan lainnya, maka fungsi `f` akan dipanggil sekali, pada 1500ms. Itulah: setelah beberapa saat fungsi tidak dipanggil maka fungsinya akan benar-benar dipanggil dengan rentang waktu 1000ms setelah pemanggilan terakhir.\n\n![](debounce.svg)\n\n...Dan itu akan mendapatkan argumen dari pemanggilan yang paling terakhir, pemanggilan lainnya akan diabaikan.\n\nIni adalah kodenya (digunakan untuk dekorator debounce dari [Lodash library](https://lodash.com/docs/4.17.15#debounce)):\n\n```js\nlet f = _.debounce(alert, 1000);\n\nf(\"a\");\nsetTimeout( () => f(\"b\"), 200);\nsetTimeout( () => f(\"c\"), 500); \n// fungsi debounce menunggu 1000ms setelah pemanggilan terakhir dan lalu menjalankan: alert(\"c\")\n```\n\nSekarang contoh yang lebih praktikal. Katakan, penggunakan mengetik sesuatu, dan kita ingin mengirim request kepada server ketika pengguna telah selesai mengetik.\n\nPada hal ini, sangat tidak berguna untuk mengirim request kepada server untuk setiap huruf yang diketik. Lagipula kita ingin menunggu, dan lalu memproses hasil ketikan pengguna.\n\nDidalam peramban, kita bisa menyetel sebuah event handler(penangan event) -- sebuah fungsi yang dipanggi untuk setiap perubahan pada kotak inputan, sebuah penangan event dipanggil sangat sering untuk setiap huruf yang diketik. Tapi jika kita ingin men`debounce`nya selama 1000ms, maka fungsinya akan dipanggil sekali, 1000ms setelah penginputan huruf terakhir.\n\n```online\n\nDidalam contoh ini, handlernya memasukan hasilnya kedalam kotak dibawah, cobalah:\n\n[iframe border=1 src=\"debounce\" height=200]\n\nLihat? inputan kedua memanggil fungsi debounce, jadi kontennya diproses setelah 1000ms dari inputan terakhir.\n```\n\nJadi, `debounce` adalah cara terbaik untuk memproses event yang terjadi berurutan: bisa tombol yang dipencet berulang-ulang, pergerakan mouse atau lainnya.\n\nFungsinya akan menunggu hingga pemanggilan terakhir, dan lalu menjalankan fungsi aslinya, lalu hasilnya akan diolah.\n\nTugasnya adalah untuk mengimplementasikan dekorator `debounce`.\n\nPetunjuk: jika kamu perhatikan, perubahan fungsinya hanya dengan menambahkan beberapa baris :)\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/solution.js",
    "content": "function throttle(func, ms) {\n\n  let isThrottled = false,\n    savedArgs,\n    savedThis;\n\n  function wrapper() {\n\n    if (isThrottled) {\n      // mengingat argumen terakhir untuk diingat setelah kondisi tidak aktif\n      savedArgs = arguments;\n      savedThis = this;\n      return;\n    }\n\n    // sebaliknya, masuk kedalam kondisi tidak aktif\n    func.apply(this, arguments);\n\n    isThrottled = true;\n\n    // mereset isThrotthled setelah penundaan\n    setTimeout(function() {\n      isThrottled = false;\n      if (savedArgs) {\n        // jika terdapat sebuah pemanggilan, savedThis/savedArgs harusnya memiliki nilai terakhir\n        // pemanggilan rekursif menjalankan fungsinya dan menyetel ulang kondisi tidak aktifnya.\n        wrapper.apply(savedThis, savedArgs);\n        savedArgs = savedThis = null;\n      }\n    }, ms);\n  }\n\n  return wrapper;\n}"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js",
    "content": "describe(\"throttle(f, 1000)\", function() {\n  let f1000;\n  let log = \"\";\n\n  function f(a) {\n    log += a;\n  }\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n    f1000 = throttle(f, 1000);\n  });\n\n  it(\"the first call runs now\", function() {\n    f1000(1); // runs now\n    assert.equal(log, \"1\");\n  });\n\n  it(\"then calls are ignored till 1000ms when the last call works\", function() {\n    f1000(2); // (ditunda - kurang dari 1000ms sejak pemanggilan terakhir)\n    f1000(3); // (ditunda - kurang dari 1000ms sejak pemanggilan terakhir)\n    // after 1000 ms f(3) call is scheduled\n    // setelah 1000ms pemanggilan f(3) dilakukan\n\n    assert.equal(log, \"1\"); // sampai sekarang hanya pemanggilan pertama yang pernah dilakukan\n\n    this.clock.tick(1000); // setelah 1000ms\n    assert.equal(log, \"13\"); // log==13, pemanggilan kepada f1000(3) telah dibuat\n  });\n\n  it(\"the third call waits 1000ms after the second call\", function() {\n    this.clock.tick(100);\n    f1000(4); // (ditunda - kurang dari 1000ms sejak pemanggilan terakhir)\n    this.clock.tick(100);\n    f1000(5); // (ditunda - kurang dari 1000ms sejak pemanggilan terakhir)\n    this.clock.tick(700);\n    f1000(6); // (ditunda - kurang dari 1000ms sejak pemanggilan terakhir)\n\n    this.clock.tick(100); // sekarang 100 + 100 + 700 + 100 = 1000ms telah berlalu\n\n    assert.equal(log, \"136\"); // pemanggilan terakhir adalah f(6)\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n});\n\ndescribe('throttle', () => {\n\n  it('runs a forwarded call once', done => {\n    let log = '';\n    const f = str => log += str;\n    const f10 = throttle(f, 10);\n    f10('once');\n\n    setTimeout(() => {\n      assert.equal(log, 'once');\n      done();\n    }, 20);\n  });\n\n});\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/solution.md",
    "content": "```js demo\nfunction throttle(func, ms) {\n\n  let isThrottled = false,\n    savedArgs,\n    savedThis;\n\n  function wrapper() {\n\n    if (isThrottled) { // (2)\n      savedArgs = arguments;\n      savedThis = this;\n      return;\n    }\n    isThrottled = true;\n\n    func.apply(this, arguments); // (1)\n\n    setTimeout(function() {\n      isThrottled = false; // (3)\n      if (savedArgs) {\n        wrapper.apply(savedThis, savedArgs);\n        savedArgs = savedThis = null;\n      }\n    }, ms);\n  }\n\n  return wrapper;\n}\n```\n\nPemanggilan kepada `throttle(func, ms)` mengembalikan `wrapper`.\n\n1. Selama pemanggilan pertama, `wrapper`nya hanya menjalankan `func` dan menyetel kondisi tidak aktif (`isThrottled = true`).\n2. Didalam kondisi ini semua pemanggilan akan diingat/disimpan didalam `savedArgs/savedThis`. Ingat baik-baik bahwa konteks dan argumennya sama-sama penting dan harus diingat/disimpan. Kita akan membutuhkannya untuk membuat panggilannya.\n3. Setelah `ms` milidetik berlalu, `setTimeout` akan berjalan. Kondisi tidak aktif dihilangkan (`isThrottled = false`) dan, jika kita memiliki daftar panggilan yang diabaikan, `wrapper` akan dieksekusi dengan argumen dan konteks yang terakhir diingat/disimpan.\n\nLangkah ketika yang berjalan bukanlah `func`, tapi `wrapper`, karena kita tidak hanya perlu mengeksekusi `func`, tapi sekali-lagi kita memasuki kondisi tidak aktif dan perlu menyetel ulang timeout.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Dekorator penutup\n\nBuatlah sebuah dekorator \"penutup\" `throttle(f, ms)` -- yang mengembalikan sebuah pembungkus.\n\nKetika fungsinya dipanggil beberapa kali, fungsinya akan melakukan pemanggilan kepada `f` maksimal sekali per `ms` milidetik.\n\nPerbedaannya dengan dekorator debounce adalah keduanya benar-benar dekorator berbeda:\n- `debounce` menjalankan fungsinya sekali setelah masa \"tidak aktif\". Bagus untuk memproses hasil akhir.\n- `throttle` menjalankan fungsinya tidak lebih banyak dari waktu `ms` yang diberikan. Bagus untuk update tersusun yang tidak terlalu sering dipanggil.\n\nDengan kata lain, `throttle` seperti seorang sekertaris yang menerima panggilan telefon, tapi menggangu bos nya (memanggil fungsi `f` asli) tidak lebih sering dari sekali per `ms` milidetik.\n\nAyo kita lihat contoh pengaplikasiannya langsung untuk mengerti lebih dalam tentang kebutuhannya dan dimana digunakannya.\n\n**Contoh, kita ingin mengetahui posisis dari pergerakan mouse.**\n\nDidalam peramban kita bisa menyetel sebuah fungsi yang berjalan untuk setiap pergerakan mouse dan mendapatkan lokasi pointernya selama mouse-nya bergerak. Selama mouse-nya bergerak terus-menerus, fungsi ini biasanya berjalan sangat sering, bisa menjadi seperti 100 kali per-detik (setiap 10ms).\n**Kota ingin meng-update beberapa informasi didalam halaman webnya ketika pointernya bergerak.**\n\n...Akan tetapi meng-update fungsi `update()` terlalu berat dilakukan untuk dijalankan terus menerus mengikuti pergerakan mouse. Tidak ada alasan yang bagus untuk meng-update lebih sering daripada sekali per 100ms.\n\nJadi kita akan membungkusnya dengan dekorator: gunakan `throttle(update, 100)` sebagai fungsi untuk berjalan setiap pergerakan mouse daripada secara langsung menggunakan `update()`. Dekoratornya akan sering dipanggil, tapi untuk pemanggilan kepada `update` akan dilakukan maksimal sekali per 100ms.\n\nSecara visual, langkah-langkahnya akan seperti ini:\n\n1. Untuk pergerakan mouse pertama dekoratornya langsung memanggil fungsi `update`. Itu penting, untuk penggunanya melihat reaksi sistemnya ketika mereka baru saja bergerak.\n2. Lalu selama mousenya bergerak, sampai `100ms` tidak akan terjadi apa-apa. Dekoratornya akan mengabaikan pemanggilannya.\n3. Setelah melewati `100ms` -- satu pemanggilan fungsi `update `terjadi dengan kondisi paling terakhir.\n4. Lalu, pada akhirnya, mousenya berhenti disuatu tempat. Dekoratornya menunggu sampai melewati `100ms` dan lalu menjalankan `update` dengan kondisi terakhir. Jadi, cukup penting, pergerakan mouse terakhir akan diproses.\n\nContoh kode:\n\n```js\nfunction f(a) {\n  console.log(a);\n}\n\n// f1000 mengirimkan pemanggilan kepada f maksimal sekali per 1000ms\nlet f1000 = throttle(f, 1000);\n\nf1000(1); // tampilkan 1\nf1000(2); // (ditahan, belum melewati 1000ms)\nf1000(3); // (ditahan, belum melewati 1000ms)\n\n// ketika 1000ms terlewati...\n// ...menampilkan 3, nilai sebelum tiga yaitu 2 akan diabaikan\n```\n\nCatatan. Argumen dan konteks `this` yang dikirimkan kepada `f1000` harus bisa dikirimkan kepada fungsi asli `f`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/09-call-apply-decorators/article.md",
    "content": "# Decorators dan forwarding, call/apply\n\nJavascript memberikan fleksibilitas yang istimewa ketika harus berurusan dengan fungsi. Mereka bisa dikirim, digunakan sebagai objek, dan sekarang kita akan melihat bagaimana *penerusan/forward* panggilan diantara mereka dan *mendekorasi/decorate* mereka.\n\n## Cache transparan\n\n\nKatakan kita mempunyai sebuah fungsi `slow(x)` yang mana adalah fungsi berat saat diolah pada CPU, tapi hasil dari fungsi tersebut stabil. Dengan kata lain, untuk `x` yang sama fungsi itu selalu mengembalikan hasil yang sama.\n\nJika fungsinya sering dipanggil, kita mungkin ingin meng-cache (mengingat) hasilnya untuk menghindari pembuangan waktu saat kalkulasi-ulang.\n\nTapi sebagai gantinya daripada menambahkan fungsionalitas lain kedalam `slow()` kita akan membuat sebuah fungsi pembungkus/wrapper, yang menambahkan cache. Seperti yang akan kita lihat, terdapat beberapa keuntungan untuk melakukan cache.\n\nIni kodenya, dan penjelasannya:\n\n```js run\nfunction slow(x) {\n  // disini terdapat task berat yang menggunakan sumberdaya CPU\n  alert(`Called with ${x}`);\n  return x;\n}\n\nfunction cachingDecorator(func) {\n  let cache = new Map();\n\n  return function(x) {\n    if (cache.has(x)) {    // jika terdapat kunci \"x\" pada cache\n      return cache.get(x); // baca hasil dari cache\n    }\n\n    let result = func(x);  // jika tidak, panggil fungsi\n\n    cache.set(x, result);  // dan cache (ingat) hasilnya\n    return result;\n  };\n}\n\nslow = cachingDecorator(slow);\n\nalert( slow(1) ); // slow(1) telah dimasukan kedalam cache\nalert( \"Again: \" + slow(1) ); // sama seperti baris sebelumnya\n\nalert( slow(2) ); // slow(2) telah dimasukan kedalam cache\nalert( \"Again: \" + slow(2) ); // sama seperti baris sebelumnya\n```\n\nDidalam kode diatas `cachingDecorator` adalah sebuah *decorator/dekorator*: sebuah fungsi spesial yang menerima fungsi dan mengubah tingkah lakunya.\n\nIdenya adalah kita bisa memanggil `cachingDecorator` dari fungsi manapun, dan itu akan mengembalikan pembungkus caching. Itu bagus, karena kita bisa mempunyai banyak fungsi yang dapat menggunakan fitur itu, dan semua yang kita butuhkan adalah menerapkan `cachingDecorator` kedalam fungsinya.\n\nDengan memisahkan caching dari kode fungsi utama kita juga bisa tetap membuat kode utama tetap sederhana.\n\nHasil dari `cachingDecorator(func)` adalah sebuah \"pembungkus/wrapper\": `function(x)` yang \"membungkus\" pemanggilan dari `func(x)` kedalam logika penyimpanan cache.\n\n![](decorator-makecaching-wrapper.svg)\n\nDari kode luar, fungsi yang dibungkus `slow` akan melakukan tetap hal yang sama. Fungsinya hanya akan menambahkan aspek caching kedalam prilakunya.\n\nUntuk meringkaskan, terdapat beberapa keuntungan untuk menggunakan `cachingDecorator` secara terpisah daripada dimasukan kedalam kode `slow` itu sendiri:\n\n- `cachingDecorator` dapat digunakan lagi. Kita bisa menerapkannya kedalam fungsi lainnnya.\n- Logika dari penyimpanan kedalam cache dipisahkan, itu tidak akan menambah kompleksitas dari `slow` sendiri.\n- Kita bisa menggunakan beberapa dekorator jika dibutuhkan.\n\n## Menggunakan \"func.call\" untuk konteksnya\n\nDekorator penyimpanan kedalam cache diatas tidak cokok untuk bekerja dengan metode objek.\n\nContoh, didalam kode dibawah `worker.slow()` akan berhenti bekerja setelah decoration:\n\n```js run\n// disini membuat worker.slow menyimpan kedalam cache\nlet worker = {\n  someMethod() {\n    return 1;\n  },\n\n  slow(x) {\n    // task yang benar-benar menggunakan banyak sumber daya CPU disini\n    alert(\"Called with \" + x);\n    return x * this.someMethod(); // (*)\n  }\n};\n\n// same code as before\nfunction cachingDecorator(func) {\n  let cache = new Map();\n  return function(x) {\n    if (cache.has(x)) {\n      return cache.get(x);\n    }\n*!*\n    let result = func(x); // (**)\n*/!*\n    cache.set(x, result);\n    return result;\n  };\n}\n\nalert( worker.slow(1) ); // metode aslinya bekerja\n\nworker.slow = cachingDecorator(worker.slow); // sekarang simpan kedalam cache\n\n*!*\nalert( worker.slow(2) ); // Whoops! Error: Cannot read property 'someMethod' of undefined\n*/!*\n```\n\nErrornya muncul pada baris `(*)` yang mencoba untuk mengakses `this.someMethod` dan gagal. Apakah kamu bisa lihat kenapa?\n\nAlasannya adalah karena pembungkusnya memanggil fungsi aslinya sebagai `func(x)` pada baris `(**)`. Dan, ketika dipanggil seperti itu, fungsinya mendapatkan `this = undefined`.\n\nKita harusnya bisa melihat kasus yang serupa jika kita mencoba menjalankan:\n\n```js\nlet func = worker.slow;\nfunc(2);\n```\n\nJadi, pembungkusnya mengirimkan pemanggilan pada metode aslinya, tapi tanpa konteks dari `this`. Karenanya akan terjadi error.\n\nCoba kita perbaiki.\n\nTerdapat sebuah metode bawaan yang spesial [func.call(context, ...args)](mdn:js/Function/call) yang mengijinkan untuk melakukan pemanggilan fungsi menyetel nilai dari `this`.\n\nSintaksnya adalah:\n\n```js\nfunc.call(context, arg1, arg2, ...)\n```\n\nItu akan menjalankan `func` yang menyediakan argumen pertama sebagai `this`, dan sisanya sebagai argumen-argumennya.\n\nUntuk menyederhanakannya, kedua pemanggilan dibawah hampir melakukan hal yang serupa:\n```js\nfunc(1, 2, 3);\nfunc.call(obj, 1, 2, 3)\n```\n\nKeduanya memanggil `func` dengan argumen `1`, `2`, dan `3`. Perbedaannya adalah `func.call` juga menyetel `this` menjadi `obj`.\n\nSebagai sebuah contoh, didalam kode dibawah kita memanggil `sayHi` didalam konteks pada objek yang berbeda: `sayHi.call(user)` menjalankan `sayHi` menyediakan `this=user`, dan baris selanjutnya menyetel `this=admin`:\n\n```js run\nfunction sayHi() {\n  alert(this.name);\n}\n\nlet user = { name: \"John\" };\nlet admin = { name: \"Admin\" };\n\n// lakukan pemanggilan untuk memberikan objek yang berbeda sebagai \"this\"\nsayHi.call( user ); // John\nsayHi.call( admin ); // Admin\n```\n\nDan disini kita menggunakan `call` untuk memanggil `say` dengan konteks dan *phrase* yang diberikan:\n\n\n```js run\nfunction say(phrase) {\n  alert(this.name + ': ' + phrase);\n}\n\nlet user = { name: \"John\" };\n\n// user menjadi this, dan \"Hello\" menjadi argumen pertama\nsay.call( user, \"Hello\" ); // John: Hello\n```\n\nDidalam kasus kita, kita bisa menggunakan `call` didalam pembungkus untuk memberikan konteks kedalam fungsi aslinya:\n\n```js run\nlet worker = {\n  someMethod() {\n    return 1;\n  },\n\n  slow(x) {\n    alert(\"Called with \" + x);\n    return x * this.someMethod(); // (*)\n  }\n};\n\nfunction cachingDecorator(func) {\n  let cache = new Map();\n  return function(x) {\n    if (cache.has(x)) {\n      return cache.get(x);\n    }\n*!*\n    let result = func.call(this, x); // \"this\" diberikan dengan benar sekarang\n*/!*\n    cache.set(x, result);\n    return result;\n  };\n}\n\nworker.slow = cachingDecorator(worker.slow); // sekarang disimpan kedalam cache\n\nalert( worker.slow(2) ); // bekerja\nalert( worker.slow(2) ); // bekerja, tidak memanggil yang aslinya (dari cache)\n```\n\nSekarang semuanya berjalan.\n\nUntuk memperjelas, kita akan melihat lebih dalam bagaimana `this` diberikan:\n\n1. Setelah dekorasi dari `worker.slow` sekarang menjadi pembungkusnya `function (x) { ... }`.\n2. Jadi ketika `worker.slow(2)` dieksekusi, pembungkusnya mendapatkan `2` sebagai sebuah argumen dan `this=worker` (sebuah objek sebelum titik).\n3. Didalam pembungkusnya, asumsikan hasilnya belum disimpan didalam cache, `func.call(this, x)` diberikan kepada `this` (`=worker`) dan argumennya (`=2`) kepada metode aslinya.\n\n## Menjadi multi-argument\n\nSekarang kita buat `cachingDecorator` menjadi lebih universal. Sampai sekarang fungsi itu hanya bekerja dengan satu-argumen.\n\nSekarang bagaimana untuk menyimpan multi-argumen metode `worker.slow` kedalam cache?\n\n```js\nlet worker = {\n  slow(min, max) {\n    return min + max; // asumsikan sebuah fungsi yang sangat berat\n  }\n};\n\n// harus mengingat pemanggilan dengan argument-yang-sama\nworker.slow = cachingDecorator(worker.slow);\n```\n\n\nSebelumnya, untuk argumen tunggal `x` kita bisa dengan melakukan `cache.set(x, result)` untuk menyimpan result-nya dan `cache.get(x)` untuk mengambilnya. Tapi kita harus mengingat hasil dari sebuah *kombinasi dari argumen-argumen* `(min, max)`. `Map` yang asli mengambil nilai tunggal sebagai kuncinya.\n\nTerdapat beberapa solusi yang bisa dilakukan:\n\n1. Implementasikan struktur data seperti-map baru yang lebih serba guna dan mengijinkan menggunakan banyak-kunci (atau gunakan third-party).\n2. Gunakan maps bercabang: `cache.set(min)` akan menjadi sebuah `Map` yang menyimpan pasangan `(max, result)`. Jadi kita bisa mendapatkan `result` sebagai `cache.get(min).get(max)`.\n3. Gabungkan kedua nilai menjadi satu. Didalam kasus tertentu kita bisa menggunakna sebuah string `\"min,max\"` sebagai kunci `Map`. Untuk fleksibilitas, kita bisa mengijinkan untuk menyediakan sebuah *fungsi hashing* untuk dekoratornya, yang mengetahui bagaimana cara membuat nilai tunggal dari banyak nilai.\n\nUntuk kebanykan penggunaan yang praktikal, varian ketiga sudahlah cukup, jadi kita akan menggunakannya.\n\nJuga kita harus memberikan bukan hanya `x`, tapi seluruh argumen-argumen didalam `func.call`. Kita panggil ulang didalam sebuah `function()` kita bisa mendapatkan pseudo-array dari argumennya sebagai `arguments`, jadi `func.call(this, x)` harus diganti dengan `func.call(this, ...arguments)`.\n\nIni adalah `cachingDecorator` yang lebih powerful:\n\n```js run\nlet worker = {\n  slow(min, max) {\n    alert(`Called with ${min},${max}`);\n    return min + max;\n  }\n};\n\nfunction cachingDecorator(func, hash) {\n  let cache = new Map();\n  return function() {\n*!*\n    let key = hash(arguments); // (*)\n*/!*\n    if (cache.has(key)) {\n      return cache.get(key);\n    }\n\n*!*\n    let result = func.call(this, ...arguments); // (**)\n*/!*\n\n    cache.set(key, result);\n    return result;\n  };\n}\n\nfunction hash(args) {\n  return args[0] + ',' + args[1];\n}\n\nworker.slow = cachingDecorator(worker.slow, hash);\n\nalert( worker.slow(3, 5) ); // bekerja\nalert( \"Again \" + worker.slow(3, 5) ); // sama (dari cache)\n```\n\nSekarang itu bekerja dengan berapapun jumlah argumen (walaupun fungsi hash harusnya disesuaikan untuk menerima argumen dengan jumlah berapapun. Cara yang menarik untuk menangani ini akan dijelaskan dibawah).\n\nTerdapat dua perubahan:\n\n- Didalam baris `(*)` memanggil `hash` untuk membuat sebuah kunci tunggal dari `arguments`. Disini kita menggunakan fungsi \"joining\" yang sederhana yang mengubah argument `(3, 5)` menjadi kunci `\"3,5\"`. Kasus kompleks yang lain mungkin membutuhkan fungsi-fungsi hashing lainnya.\n- Lalu `(**)` menggunakan `func.call(this, ...arguments)` untuk memberikan konteks dan seluruh argumen yang pembungkusnya dapatkan (tidak hanya yang pertama) dari fungsi aslinya.\n\n## func.apply\n\nDaripada `func.call(this, ...arguments)` kita bisa gunakan `func.apply(this, arguments)`.\n\nSintaks dari metode bawaannya [func.apply](mdn:js/Function/apply) adalah:\n\n```js\nfunc.apply(context, args)\n```\n\nKode diatas menjalankan `func` dan menyetel `this=context` dan menggunakan objek yang seperti array `args` sebagai daftar dari argumen-argumen.\n\nPerbedaan sintaks antara `call` dan `apply` adalah bahwa `call` mengharapkan sebuah daftar dari argumen-argumen, sementara `apply` menerima objek yang seperti-array didalamnya.\n\nJadi kedua pemanggilan dibawah hampir sama:\n\n```js\nfunc.call(context, ...args); // mengirimkan sebuah array sebagai daftar dengan sintaks spread\nfunc.apply(context, args);   // sama seperti pemanggilan call\n```\n\nHanya terdapat perbedaan yang tipis:\n\n- Sintaks spread `...` mengijinkan untuk mengirimkan *iterable* `args` sebagai list untuk `call`.\n- `apply` hanya menerima `args` yang *seperti-array*.\n\nJadi, dimana kita mengharapkan sebuah iterasi, gunakan `call`, dan dimana kita menggunakan seperti-array, gunakan `apply`.\n\nDan untuk objek yang bisa diiterasi dan seperti-array, seperti array yang asli, kita bisa gunakan keduanya, tapi `apply` akan lebih cepat, karena kebanyakan mesin Javascript secara internal mengoptimasi `apply` lebih baik.\n\nMengirimkan seluruh argumen bersamaan dengan konteks ke fungsi lainnya dipanggil dengan *call forwarding*.\n\nIni adalah contoh paling sederhana dari *call forwarding*:\n\n```js\nlet wrapper = function() {\n  return func.apply(this, arguments);\n};\n```\n\nKetika sebuah kode eksternal memanggil `wrapper` yang seperti diatas, pemanggilan itu tidak bisa dibedakan dengan pemanggilan dari fungsi asli `func`.\n\n## Meminjam sebuah metode [#method-borrowing]\n\nSekarang kita buat satu perubahan minor didalam fungsi hashing:\n\n```js\nfunction hash(args) {\n  return args[0] + ',' + args[1];\n}\n```\n\nSeperti yang sekarang, fungsi diatas hanya akan bekerja dengan dua argumen. Fungsi diatas akan lebih baik jika dapat menerima berapapun jumlah dari `args`.\n\nSolusi naturalnya harusnya dengan menggunakan metode [arr.join](mdn:js/Array/join):\n\n```js\nfunction hash(args) {\n  return args.join();\n}\n```\n\n...Sayangnya, hal diatas tidak akan bekerja. karena kita memanggil `hash(arguments)`, dan objek `arguments` adalah hal yang bisa diiterasi dan hal yang seperti array, tapi bukanlah array asli.\n\njadi memanggil `join` tentu tidak akan bekerja, seperti yang bisa kita lihat dibawah:\n\n```js run\nfunction hash() {\n*!*\n  alert( arguments.join() ); // Error: arguments.join is not a function\n*/!*\n}\n\nhash(1, 2);\n```\n\nTetap, terdapat sebuah cara yang mudah untuk menggunakan array join:\n\n```js run\nfunction hash() {\n*!*\n  alert( [].join.call(arguments) ); // 1,2\n*/!*\n}\n\nhash(1, 2);\n```\n\nCaranya bernama *method borrowing*.\n\nKita menggunakan (borrow/meminjam) metode join dari array biasa (`[].join`) dan gunakan `[].join.call` untuk menjalankannya didalam konteks dari `arguments`.\n\nKenapa hal itu bisa bekerja?\n\nItu karena algoritma internal dari metode native `arr.join(glue)` sangatlah sederhana.\n\nDiambil dari spesifikasi hampir \"as-is(apa adanya)\":\n\n1. Biarkan `glue` menjadi argumen pertama atau, jika tidak ada argumen, maka sebuah koma `\",\"`.\n2. Biarkan `result` menjadi sebuah string kosong.\n3. Masukan `this[0]` kedalam `result`.\n4. Masukan `glue` dan `this[1]`.\n5. Masukan `glue` dan `this[2]`.\n6. ...lakukan terus sampai item dari `this.length` ditempel.\n7. Kembalikan `result`.\n\nJadi, secara tekniks itu akan menggunakan `this` dan menggabungkan `this[0]`, `this[1]` ...lainnya bersama. Itu secara sengaja ditulis dengan cara yang mengijinkan hal yang seperti array `this` (bukan kebetulan, banyak metode lainnya mengikuti cara ini). Itulah kenapa hal ini bekerja juga dengan `this=arguments`.\n\n## Decorators and properti fungsi\n\nSecara umum mengganti sebuah fungsi atau metode dengan yang telah diubah adalah hal yang aman, kecuali untuk satu hal kecil. Jika fungsi aslinya memiliki properti didalamnya `func.calledCount` atau apapun, maka fungsi yang telah diubah tidak akan memilikinya. Karena itu adalah sebuah pembungkus. Jadi haruslah hati-hati saat menggunakannya.\n\nContoh, didalam contoh diatas jika fungsi `slow` memiliki properti apapun didalamnya, maka `cachingDecorator(slow)` adalah sebuah pembungkus tanpa properti itu.\n\nBeberapa dekorator mungkin menyediakan propertinya sendiri. Misalnya sebuah dekorator mungkin menghitung berapa kali fungsinya dipanggil dan berapa lama pemanggilannya, dan mengetahui informasi ini lewat pembungkus properti.\n\nTerdapat sebuah cara untuk membuat dekorator yang tetap menyimpan akses kepada properti fungsi, tapi hal ini membutuhkan objek spesial `Proxy` untuk membungkus fungsinya. Kita akan pelajari nanti dalam artikel <info:proxy#proxy-apply>.\n\n## Ringkasan\n\n*Dekorator* adalah sebuah pembungkus fungsi yang mengubah prilaku fungsi tersebut. Pekerjaan utamanya tetap untuk membawa fungsinya.\n\nDekorator bisa dilihat sebagai \"fitur\" atau \"aspek\" yang bisa ditambahkan kedalam fungsi. Kita bisa menambahkan satu atau banyak. Dan semuanya tanpa mengubah kode dari fungsinya sendiri.\n\nUntuk mengimplementasikan `cachingDecorator`, kita telah mempelajari metode:\n\n- [func.call(context, arg1, arg2...)](mdn:js/Function/call) -- memanggil `func` dengan konteks dan argumen yang diberikan.\n- [func.apply(context, args)](mdn:js/Function/apply) -- memanggil `func` mengirimkan `context` sebagai this dan hal yang seperti array `args` kedalam sebuah daftar dari argumen. \n\n*call forwarding* biasanya digunakan dengan `apply`:\n\n```js\nlet wrapper = function() {\n  return original.apply(this, arguments);\n};\n```\n\nKita juga melihat contoh dari *method borrowing* ketika kita mengambil metode dari sebuah objek dan `call/memanggil`nya didalam konteks dari objek lain. Hal itu cukup umum untuk mengambil metode array dan mengaplikasikannya kepada `arguments`. Alternatif lainnya adalah untuk menggukanan objek parameter rest yang mana adalah sebuah array asli.\n\nTerdapat beberapa dekorator yang tersedia. Pecahkan seluruh task untuk mengetahui seberapa paham kamu tentang dekorator tersebut didalam bab ini.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/solution.md",
    "content": "Jawabannya: `null`.\n\n\n```js run\nfunction f() {\n  alert( this ); // null\n}\n\nlet user = {\n  g: f.bind(null)\n};\n\nuser.g();\n```\n\nKonteks dari pengikatan fungsi sangat sulit diperbaiki. Tidak ada cara untuk merubahnya dilain waktu.\n\nJadi bahkan ketika kita menjalankan `user.g()`, fungsi aslinya dipanggil dengan `this=null`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/2-write-to-object-after-bind/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Ikat fungsi sebagai sebuah metode\n\nApakah keluarannya?\n\n```js\nfunction f() {\n  alert( this ); // ?\n}\n\nlet user = {\n  g: f.bind(null)\n};\n\nuser.g();\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/3-second-bind/solution.md",
    "content": "Jawabannya: **John**.\n\n```js run no-beautify\nfunction f() {\n  alert(this.name);\n}\n\nf = f.bind( {name: \"John\"} ).bind( {name: \"Pete\"} );\n\nf(); // John\n```\n\nObjek eksotis [bound function](https://tc39.github.io/ecma262/#sec-bound-function-exotic-objects) yang dikembalikan oleh `f.bind(...)` mengingat konteksnya (dan argumen jika ada) hanya pada waktu pembuatan.\n\nSebuah fungsi tidak bisa diikat-ulang.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/3-second-bind/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Pengikatan kedua\n\nBisakah kita merubah`this` dengan pengikatan tambahan?\n\nApakah yang akan menjadi keluarannya?\n\n```js no-beautify\nfunction f() {\n  alert(this.name);\n}\n\nf = f.bind( {name: \"John\"} ).bind( {name: \"Ann\" } );\n\nf();\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/4-function-property-after-bind/solution.md",
    "content": "Jawabannya: `undefined`.\n\nHasil dari `bind` adalah objek lainnya. Objek tersebut tidak memiliki properti `test`.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/4-function-property-after-bind/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Properti fungsi setelah pengikatan\n\nTerdapat sebuah nilai didalam properti dari sebuah fungsi. Apakah properti tersebut akan berubah setelah `bind`? Kenapa, atau kenapa tidak?\n\n```js run\nfunction sayHi() {\n  alert( this.name );\n}\nsayHi.test = 5;\n\n*!*\nlet bound = sayHi.bind({\n  name: \"John\"\n});\n\nalert( bound.test ); // apakah keluarannya? kenapa?\n*/!*\n```\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/5-question-use-bind/solution.md",
    "content": "\nErrornya muncul karena `ask` mendapatkan fungsi `loginOk/loginFail` tanpa objeknya.\n\nketika `ask` memanggil, `loginOk/loginFail` mengasumsikan bahwa `this=undefined`.\n\nAyo kita coba `bind` konteksnya:\n\n```js run\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  loginOk() {\n    alert(`${this.name} logged in`);\n  },\n\n  loginFail() {\n    alert(`${this.name} failed to log in`);\n  },\n\n};\n\n*!*\naskPassword(user.loginOk.bind(user), user.loginFail.bind(user));\n*/!*\n```\n\nSekarang kodenya berjalan.\n\nAlternatif lainnya bisa menggunakan:\n```js\n//...\naskPassword(() => user.loginOk(), () => user.loginFail());\n```\n\nContoh diatas biasanya bekerja dan terlihat lebih rapih.\n \nCara  ini sedikit kurang bisa digunakan didalam situasi yang lebih kompleks dimana variabel `user` mungkin berubah *setelah* `askPassword` dipanggil, tapi *sebelum* pengguna menjawab dan memanggil `() => user.loginOk()`.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/5-question-use-bind/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Perbaiki sebuah fungsi yang telah kehilangan \"this\"\n\nPemanggilan kepada `askPassword()` didalam kode dibawah harus memeriksa passwordnya dan lalu memanggil `user.loginOk/loginFail` tergantung dari jawabannya.\n\nTapi pemanggilan itu mengembalikan sebuah error. kenapa?\n\nPerbaiki baris yang di tandai agar kodenya dapat berjalan dengan benar (baris lainnya tidak perlu diubah).\n\n```js run\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  loginOk() {\n    alert(`${this.name} logged in`);\n  },\n\n  loginFail() {\n    alert(`${this.name} failed to log in`);\n  },\n\n};\n\n*!*\naskPassword(user.loginOk, user.loginFail);\n*/!*\n```\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/6-ask-partial/solution.md",
    "content": "\n\n1. Funakan fungsi pembungkus, lebih jelasnya gunakanlah fungsi arrow:\n\n    ```js \n    askPassword(() => user.login(true), () => user.login(false)); \n    ```\n\n    Sekarang fungsi `user dapat diambil dari variabel luar dan dijalankan dengan normal.\n\n2. Atau buat sebuah fungsi mini dari `user.login` yang menggunakan `user` sebagai konteksnya dan yang mana mempunyai argumen pertama yang benar:\n\n\n    ```js \n    askPassword(user.login.bind(user, true), user.login.bind(user, false)); \n    ```\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/6-ask-partial/task.md",
    "content": "nilai penting: 5\n\n---\n\n# Pengaplikasian parsial untuk login\n\nTugas yang ini adalah varian yang sedikit lebih sulit daripada <info:task/question-use-bind>. \n\nObjek `user`nya telah dimodifikasi. Sekarang daripada menggunakan dua fungsi `loginOk/loginFail`, sekarang hanya memiliki satu fungsi `user.login(true/false)`.\n\nApa yang harus kita kirimkan kedalam `askPassword` di kode dibawah, apakah harus memanggul `user.login(true)` sebagai `ok` dan `user.login(false)` sebagai `fail`?\n\n```js\nfunction askPassword(ok, fail) {\n  let password = prompt(\"Password?\", '');\n  if (password == \"rockstar\") ok();\n  else fail();\n}\n\nlet user = {\n  name: 'John',\n\n  login(result) {\n    alert( this.name + (result ? ' logged in' : ' failed to log in') );\n  }\n};\n\n*!*\naskPassword(?, ?); // ?\n*/!*\n```\n\nUbahlah kodenya hanya pada bagian yang ditandai.\n\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/article.md",
    "content": "libs:\n  - lodash\n\n---\n\n# Function binding\n\nKetika mengirimkan metode objek sebagai callback, seperti `setTimeout`, terdapat sebuah masalah: \"kehilangan `this`\".\n\nDidalam chapter ini kita akan belajar cara memperbaikinya.\n\n## Kehilangan \"this\"\n\nKita sudah melihat beberapa contoh saat kehilangan `this`. Sekalinya sebuah metode dikirim kebagian kode lain dengan terpisah dari objeknya -- `this` akan menghilang dari metodenya.\n\nIni adalah bagaimana hal itu terjadi dengan `setTimeout`:\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nsetTimeout(user.sayHi, 1000); // Hello, undefined!\n*/!*\n```\n\nSeperti yang bisa kita lihat, keluarannya tidak menampilkan \"John\" sebagai `this.firstName`, tapi menampilkan `undefined`!\n\nItu karena `setTimeout` mendapatkan fungsi `user.sayHi`, terpisah dari objeknya. Baris terakhir bisa ditulis ulang sebagai:\n\n```js\nlet f = user.sayHi;\nsetTimeout(f, 1000); // kehilangan konteks dari user\n```\n\nMetode `setTimeout` didalam peramban sedikit spesial: metode tersebut menyetel `this=window` untuk pemanggilan fungsi (untuk Node.js, `this` menjadi objek timer, tapi tidak terlalu penting disini). Jadi untuk `this.firstName` metodenya jadi mendapatkan `window.firstName`, yang mana tidak ada. Dalam kasus serupa lainnya `this` akan menjadi `undefined`.\n\nTugasnya cukup tipikal -- kita ingin mengirim metode objek ke bagian kode lainnya (disini -- kepada penjadwal/setTimeout) dimana metodenya akan dipanggil. Bagaimana cara untuk memeriksa konteksnya dipanggil dengan benar?\n\n## Solusi 1: pembungkus\n\nSolusi sederhananya adalah untuk menggunakan fungsi pembungkus:\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nsetTimeout(function() {\n  user.sayHi(); // Hello, John!\n}, 1000);\n*/!*\n```\n\nKode diatas bekerja, karena `user` didapatkan dari lingkungan leksikal terluar, dan lalu memanggil metodenya secara normal.\n\nSolusi yang sama, tapi lebih pendek:\n\n```js\nsetTimeout(() => user.sayHi(), 1000); // Hello, John!\n```\n\nTerlihat bagus, tapi sedikit memiliki kerentanan yang akan muncul pada struktur kodenya.\n\nBagaimana jika sebelum `setTimeout` berjalan (terdapat penundaan selama satu detik!) nilai `user` untuk berubah? Maka, tiba-tiba,fungsinya akan memanggil objek yang salah.\n\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\nsetTimeout(() => user.sayHi(), 1000);\n\n// ...nilai dari user berubah sebelum 1 detik!\nuser = {\n  sayHi() { alert(\"Another user in setTimeout!\"); }\n};\n\n// setTimeout menggunakan user yang berbeda!\n```\n\nSolusi selanjutnya akan menjamin hal seperti diatas tidak akan terjadi.\n\n## Solusi 2: bind\n\nFungsi menyediakan sebuah metode bawaan [bind](mdn:js/Function/bind) yang mengijinkan untuk membernarkan `this`.\n\nSintaks dasarnya adalah:\n\n```js\n// contoh sintaks yang lebih kompleks akan kita segera lihat\nlet boundFunc = func.bind(context);\n```\n\nhasil dari `func.bind(contenxt)` adalah sesuatu yang terlihat seperti fungsi spesial atau bisa disebut dengan \"objek eksotik\", yang dapat dipanggil sebagai fungsi dan dapat melanjutkan pemanggilan kepada `func` sambil menyetel `this=context`.\n\nDengan kata lain, memanggil `boundFunc` sama seperti `func` dengan nilai `this` yang tetap.\n\nContoh, disini `funcUser` mengirimkan sebuah panggilan kepada `func` dengan `this=user`:\n\n```js run  \nlet user = {\n  firstName: \"John\"\n};\n\nfunction func() {\n  alert(this.firstName);\n}\n\n*!*\nlet funcUser = func.bind(user);\nfuncUser(); // John  \n*/!*\n```\n\nDisini `func.bin(user)` sebagai sebuah varian dari `func`, dengan nilai tetap `this=user`.\n\nSeluruh argumen dikirim kepada `func` asli \"sebagaimana adanya\", contoh:\n\n```js run  \nlet user = {\n  firstName: \"John\"\n};\n\nfunction func(phrase) {\n  alert(phrase + ', ' + this.firstName);\n}\n\n// bind this to user\nlet funcUser = func.bind(user);\n\n*!*\nfuncUser(\"Hello\"); // Hello, John (argumen \"Hello\" dikirim, dan this=user)\n*/!*\n```\n\nSekarang kita coba dengan menggunakan metode objek:\n\n\n```js run\nlet user = {\n  firstName: \"John\",\n  sayHi() {\n    alert(`Hello, ${this.firstName}!`);\n  }\n};\n\n*!*\nlet sayHi = user.sayHi.bind(user); // (*)\n*/!*\n\n// bisa dijalankan tanpa objek\nsayHi(); // Hello, John!\n\nsetTimeout(sayHi, 1000); // Hello, John!\n\n// bahkan jika nilai dari user berubah sebelum 1 detik\n// sayHi menggunakan nilai yang telah diikat, yang mana telah mereferensi kepada objek yang lama\nuser = {\n  sayHi() { alert(\"Another user in setTimeout!\"); }\n};\n```\n\nDidalam baris `(*)` kita menggunakan metode `user.sayHi` dan mengikatkannta kepada `user`. `sayHi` adalah sebuah fungsi \"terikat\", yang bisa dipanggil sendiri atau dikirimkan kepada `setTimeout` -- itu tidaklah penting, yang penting adalah konteksnya tepat.\n\nDisini kita bisa melihat argumen yang dikirimkan \"seperti adanya\", hanya saja `this` nilainya menjadi tetap oleh `bind`:\n\n```js run\nlet user = {\n  firstName: \"John\",\n  say(phrase) {\n    alert(`${phrase}, ${this.firstName}!`);\n  }\n};\n\nlet say = user.say.bind(user);\n\nsay(\"Hello\"); // Hello, John (argumen \"Hello\" dikirim untuk digunakan)\nsay(\"Bye\"); // Bye, John (\"Bye\" dikirim untuk digunakan)\n```\n\n````smart header=\"Metode yang bermanfaat: `bindAll`\"\nJika sebuah objek mempunyai beberapa metode dan kita berencana untuk mengirimkannya kebagian kode lain secara terus-menerus, kita bisa mengikatkannya didalam sebuah perulangan:\n\n```js\nfor (let key in user) {\n  if (typeof user[key] == 'function') {\n    user[key] = user[key].bind(user);\n  }\n}\n```\n\nLibrari Javascript juga menyediakan fungsi untuk memudahkan pengikatan/binding masal, contoh [_.bindAll(object, methodNames)](http://lodash.com/docs#bindAll) didalam lodash.\n\n\n## Partial functions/Fungsi sebagian\n\nSampai sekarang kita hanya berbicara tentang binding/pengikatan `this`. Ayo kita lihat lebih dalam.\n\nKita bisa mengikat bukan hanya `this`, tapi juga argumen. Yang mana sangat jarang digunakan, tapi terkadang cukup mudah digunakan.\n\nSintaks penuh dari `bind`:\n\n```js\nlet bound = func.bind(context, [arg1], [arg2], ...);\n```\n\nYang mana mengijinkan kita untuk mengikat konteks sebagai `this` dan memulai argumen dari sebuah fungsi.\n\nContoh, kita mempunyai sebuah fungsi perkalian `mul(a, b)`:\n\n```js\nfunction mul(a, b) {\n  return a * b;\n}\n```\n\nKita gunakan `bind` untuk membuat sebuah fungsi `double` didalamnya:\n\n```js run\nfunction mul(a, b) {\n  return a * b;\n}\n\n*!*\nlet double = mul.bind(null, 2);\n*/!*\n\nalert( double(3) ); // = mul(2, 3) = 6\nalert( double(4) ); // = mul(2, 4) = 8\nalert( double(5) ); // = mul(2, 5) = 10\n```\n\nPanggilan pada `mul.bind(null, 2)` membuat function `double` baru yang memberikan panggilan terhadap `mul`, memperbaiki `null` sebagai konteksnya dan `2` sebagai argumen pertamanya. Argumen-argumen lebih lanjut yang diberikan \"as is/sebagaimana adanya\".\n\nItu dipanggil [partial function application](https://en.wikipedia.org/wiki/Partial_application) -- kita membuat sebuah fungsi baru dengan memperbaiki beberapa parameter dari yang sudah ada.\n\nHarap dicatat bahwa disini kita tidak menggunakan `this`. Tapi `bind` memerlukannya, jadi kita harus meletakkan di dalam sesuatu seperti `null`.\n\nFungsi `triple` di dalam kode dibawah ini melipatkan tiga kali lipat nilai tersebut:\n\n```js run\nfunction mul(a, b) {\n  return a * b;\n}\n\n*!*\nlet triple = mul.bind(null, 3);\n*/!*\n\nalert( triple(3) ); // = mul(3, 3) = 9\nalert( triple(4) ); // = mul(3, 4) = 12\nalert( triple(5) ); // = mul(3, 5) = 15\n```\n\nKenapa kita umumnya membuat fungsi parsial?\n\nManfaatnya bahwa kita dapat membuat sebuah fungsi independen dengan nama yang dapat dibaca (`double`, `triple`). Kita bisa menggunakannya dan tidak menyediakan argumen pertamanya setiap saat karena sudah diperbaiki dengan `bind`.\n\nDalam kasus lain, aplikasi parsial berguna saat kita punya sebuah fungsi generik dan menginginkan varian yang kurang universal untuk kenyamanan.\n\nContoh, kita punya sebuah fungsi `send(from, to, text)`. Kemudian, di dalam objek `user` kita mungkin ingin menggunakan varian parsial darinya: `sendTo(to, text)` yang dikirim dari user saat ini.\n\n## Menjadi parsial tanpa konteks\n\nBagaimana jika kita ingin memperbaiki beberapa argumen, tetapi bukan konteks `this`? Contoh, untuk sebuah method objek.\n\n`bind` yang asli tidak mengizinkan itu. Kita tidak bisa begitu saja mengabaikan konteks dan lompat ke argumen.\n\nUntungnya, fungsi `partial` untuk mengikat argumen saja dapat dengan mudah diterapkan.\n\nSeperti ini:\n\n```js run\n*!*\nfunction partial(func, ...argsBound) {\n  return function(...args) { // (*)\n    return func.call(this, ...argsBound, ...args);\n  }\n}\n*/!*\n\n// Usage:\nlet user = {\n  firstName: \"John\",\n  say(time, phrase) {\n    alert(`[${time}] ${this.firstName}: ${phrase}!`);\n  }\n};\n\n// tambahkan method parsial dengan waktu tetap\nuser.sayNow = partial(user.say, new Date().getHours() + ':' + new Date().getMinutes());\n\nuser.sayNow(\"Hello\");\n// Something like:\n// [10:00] John: Hello!\n```\nHasil dari panggilan `partial(func[, arg1, arg2...])` yaitu sebuah pembungkus `(*)` yang memanggil `func` dengan:\n- `this` sama seperti yang didapat  (`user.sayNow` menyebutnya `user`)\n- Lalu berikan `...argsBound` -- argumen dari panggilan `partial` yaitu (`\"10:00\"`)\n- Lalu berikan `...args` -- argumen yang diberikan ke pembungkus (`\" Hello \"`)\n\nSangat mudah melakukannya dengan sintaks penyebaran, bukan?\n\nJuga ada implementasi [_.partial](https://lodash.com/docs#pihak) yang siap dari perpustakaan lodash.\n\n## Kesimpulan\n\nMethod `func.bind(context, ...args)` mengembalikan sebuah \"varian terikat\" dari function `func` yang memperbaiki konteks` this` dan argumen pertama jika diberikan.\n\nBiasanya kita menerapkan `bind` untuk memperbaiki `this` untuk sebuah method objek, sehingga kita bisa memberikannya ke suatu tempat. Misalnya, ke `setTimeout`.\n\nKetika kita memperbaiki beberapa argumen dari function yang ada, fungsi yang dihasilkan (less universal) disebut *partially applied* atau *partial*.\n\nParsial lebih mudah digunakan ketika kita tidak ingin mengulangi argumen yang sama berulang kali. Seperti jika kita memiliki fungsi `send (from, to)`, dan `from` harus selalu sama untuk tugas kita, kita bisa mendapatkan sebuah partial dan melanjutkannya.\n"
  },
  {
    "path": "1-js/06-advanced-functions/10-bind/head.html",
    "content": "<script>\nfunction mul(a, b) {\n  return a * b;\n};\n\nfunction ask(question, answer, ok, fail) {\n  let result = prompt(question, '');\n  if (result.toLowerCase() == answer.toLowerCase()) ok();\n  else fail();\n}\n\nfunction bind(func, context /*, args*/) {\n  let bindArgs = [].slice.call(arguments, 2); // (1)\n  function wrapper() {                        // (2)\n    let args = [].slice.call(arguments);\n    let unshiftArgs = bindArgs.concat(args);  // (3)\n    return func.apply(context, unshiftArgs);  // (4)\n  }\n  return wrapper;\n}\n</script>"
  },
  {
    "path": "1-js/06-advanced-functions/12-arrow-functions/article.md",
    "content": "# Membahas Kembali Fungsi Arrow\n\nAyo kita kunjungi kembali Fungsi Arrow.\n\nFungsi arrow bukanlah cuma bertujuan untuk menyingkat penulisan fungsi. Namun mereka memiliki fitur yang berguna dalam kondisi tertentu.\n\n\nJavascript memiliki banyak kondisi yang dimana kita membutuhkan penulisan fungsi kecil yang yang dijalankan disuatu tempat.\n\nSebagai contoh:\n\n- `arr.forEach(func)` -- `func` dijalankan oleh `forEach` untuk setiap item pada array.\n- `setTimeout(func)` -- `func` dijalankan oleh penjadwal bawaan.\n- ...dan ada banyak contoh lain.\n\nSudah menjadi jiwa dasar javascript untuk membuat fungsi dan menjalankannya disuatu tempat.\n\nDan ada kalanya pada suatu fungsi, kita biasanya tidak ingin meninggalkan konteks dimana fungsi itu berjalan. dan situlah dimana penggunaan fungsi arrow menjadi berguna.\n\n## Fungsi Arrow tidak memiliki \"this\"\n\nSeperti yang kita ingat pada chapter <info:object-methods>, fungsi arrow tidak memiliki `this`. jika `this` diakses, itu diambil dari fungsi normal diluar. (bukan dari fungsi arrow)\n\nSebagai contoh, kita bisa menggunakannya untuk mengiterasi apa yang ada didalam objek method:\n\n```js run\nlet group = {\n  title: \"Our Group\",\n  students: [\"John\", \"Pete\", \"Alice\"],\n\n  showList() {\n*!*\n    this.students.forEach(\n      student => alert(this.title + ': ' + student)\n    );\n*/!*\n  }\n};\n\ngroup.showList();\n```\n\nPada `forEach` disini, fungsi arrow digunakan, jadi `this.title` hasilnya akan sama persis dengan fungsi diluar `showList`. Yaitu : `group.title`.\n\nJika kita menggunakan fungsi \"normal\", maka akan terjadi eror:\n\n```js run\nlet group = {\n  title: \"Our Group\",\n  students: [\"John\", \"Pete\", \"Alice\"],\n\n  showList() {\n*!*\n    this.students.forEach(function(student) {\n      // Error: Cannot read property 'title' of undefined\n      alert(this.title + ': ' + student);\n    });\n*/!*\n  }\n};\n\ngroup.showList();\n```\n\nError tersebut terjadi karena `forEach` berjalan dengan fungsi `this=undefined` sebagai bawaannya, jadi upaya untuk mengakses `undefined.title` terjadi.\n\nItu tidak mempengaruhi fungsi arrow, karena mereka tidak memiliki `this`.\n\n```warn header=\"Fungsi arrow tidak bisa berjalan dengan `new`\"\nTidak memiliki `this` sebagai bawaannya, berarti memiliki keterbatasan lainnya: fungsi arrow tidak bisa digunakan sebagai fungsi constructors. Mereka tidak bisa dipanggil dengan `new`.\n```\n\n```smart header=\"Arrow functions VS bind\"\nTerdapat sedikit perbedaan antara fungsi arrow '=>' dan sebuah fungsi normal yang dipanggil dengan `.bind(this`):\n\n- `.bind(this)` membuat sebuah \"versi terikat\" dari fungsi itu.\n- The arrow `=>` tidak membuat keterikatan. Fungsi itu secara dasar tidak memiliki `this`. Pencarian dari `this` dibuat sama persis dengan sebuah pencarian variabel normal: yaitu pada luar lexical environment. \n```\n\n## Fungsi arrow tidak memiliki \"arguments\"\n\nFungsi arrow juga tidak memiliki variabel `arguments` .\n\nItu bagus untuk decorators, ketika kita butuh untuk meneruskan panggilan dengan `this` dan `arguments` yang sekarang. \n\nSebagai contoh, `defer(f, ms)` mendapatkan sebuah fungsi dan mengembalikan sebuah wrapper disekitarnya yang menunda panggilan selama `ms` milisekon:\n\n```js run\nfunction defer(f, ms) {\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(who) {\n  alert('Hello, ' + who);\n}\n\nlet sayHiDeferred = defer(sayHi, 2000);\nsayHiDeferred(\"John\"); // Hello, John Setelah 2 detik\n```\n\nSama juga tanpa menggunakan fungsi arrow berupa:\n\n```js\nfunction defer(f, ms) {\n  return function(...args) {\n    let ctx = this;\n    setTimeout(function() {\n      return f.apply(ctx, args);\n    }, ms);\n  };\n}\n```\n\nDisini kita butuh untuk membuat variabel tambahan `args` dan `ctx` agar fungsi didalam `setTimeout` bisa mengambilnya.\n\n## Kesimpulan\n\nFungsi arrow:\n\n- Tidak memiliki `this`\n- Tidak memiliki `arguments`\n- Tidak bisa dipanggil dengan `new`\n- Dia juga tidak memiliki `super`, kita belum mempelajarinya. Tapi kita akan mempelajarinya di <info:class-inheritance>\n\nItu dikarenakan fungsi arrow dibuat untuk pembentukan fungsi kode pendek yang tidak memiliki \"konteks\" pribadi, melainkan bekerja pada konteks dimana fungsi itu bekerja. Dan mereka bekerja sangat baik pada kasus tersebut.\n"
  },
  {
    "path": "1-js/06-advanced-functions/index.md",
    "content": "# Penggunaan lanjutan fungsi\n"
  },
  {
    "path": "1-js/07-object-properties/01-property-descriptors/article.md",
    "content": "\n# Properti flag dan Deskriptor\n\nSeperti yang kita ketahui, objek dapat menyimpan banyak properti.\n\nSampai sekarang, sebuah properti hanyalah pasangan nilai dan kunci bagi kita. Tapi, sebuah properti objek sebenarnya lebih fleksibel dan memiliki banyak kegunaan.\n\nPada bab ini kita akan mempelajari konfigurasi tambahan dan pada bab selanjutnya kita akan melihat bagaimana secara samar mengubahnya menjadi fungsi getter atau setter.\n\n## Properti Flag\n\nProperti Objek, selain sebuah **`Nilai`**, memiliki tiga atribut spesial (yang dinamakan \"flags\"):\n\n- **`writable`** -- jika `benar`, maka nilai nya bisa diubah,  jika tidak maka hanya bsa dibaca.\n- **`enumerable`** -- jika `benar`, maka akan dicantumkan pada daftar perulangan, jika tidak maka tidak akan dicantumkan. (Menentukan apakah bisa melakukan perulangan dengan properti objek tersebut)\n- **`configurable`** -- jika `benar`, properti itu dapat dihapus dan attribute-attributenya bisa diubah-ubah, jika tidak maka tidak bisa dihapus dan diubah.\n\n**catatan:** *`benar` disini biasanya di gambarkan sebagai boolean `true` dan `salah` digambarkan sebagai boolean `false` pada javascript.*\n\nKita belum melihat mereka, karena biasanya mereka tidak muncul. Ketika kita membuat sebuah properti \"dengan cara biasa\", Semua dari tiga attribut diatas biasanya bernilai `benar`.namun, kita juga bisa mengubahnya kapan pun kita mau.\n\nPertama, mari kita lihat bagaimana cara mendapatkan properti flag tersebut.\n\nPada Method [Object.getOwnPropertyDescriptor](mdn:js/Object/getOwnPropertyDescriptor) memperbolehkan kita untuk melakukan query terhadap informasi *komplit* dari sebuah properti.\n\nSintaksnya adalah:\n```js\nlet descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);\n```\n\n`obj`\n: Objek yang akan kita ambil informasinya.\n\n`propertyName`\n: Nama dari properti tersebut.\n\nNilai yang akan dikembalikan disebut sebagai \"properti deskriptor\" dari objek: didalamnya mengandung nilai dan semua flag dari properti tersebut.  \n\nSebagai contoh:\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nlet descriptor = Object.getOwnPropertyDescriptor(user, 'name');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/* property descriptor:\n{\n  \"Nilai\": \"John\",\n  \"writable\": true,\n  \"enumerable\": true,\n  \"configurable\": true\n}\n*/\n```\n\nUntuk mengganti flag tersebut, kita dapat menggunakan [Object.defineProperty](mdn:js/Object/defineProperty).\n\nSintaksnya adalah:\n\n```js\nObject.defineProperty(obj, propertyName, descriptor)\n```\n\n`obj`, `propertyName`\n: Objek dan nama properti yang akan diterapkan deskriptornya.\n\n`descriptor`\n: Properti deskriptor objek yang akan digunakan.\n\nJika properti tersebut ada, `defineProperty` akan memperbarui flagnya. namun, itu akan membuat properti dengan nilai yang diberikan dan flag properti tersebut; pada kasus itu, jika sebuah flag  tidak disediakan, maka itu akan diasumsikan dengan nilai `false` (salah).\n\nSebagai contoh, sebuah properti `name` dibuat dengan semua nilai flag yang salah:\n\n```js run\nlet user = {};\n\n*!*\nObject.defineProperty(user, \"name\", {\n  value: \"John\"\n});\n*/!*\n\nlet descriptor = Object.getOwnPropertyDescriptor(user, 'name');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/*\n{\n  \"value\": \"John\",\n*!*\n  \"writable\": false,\n  \"enumerable\": false,\n  \"configurable\": false\n*/!*\n}\n */\n```\n\nbandingkan dengan \"pembuatan cara biasa\" `user.name` diatas: sekarang semua nilai flagnya berbentuk salah. Jika itu yang bukan kita mau maka kita sebaiknya merubahnya menjadi `true` pada `deskriptor`nya.\n\nSekarang mari kita lihat efek flag tersebut dengan contoh.\n\n## Non-writable\n\nAyo kita buat `user.name` menjadi non-writable (tidak bisa diatur dan ditetapkan kembali) dengan mengubah flag `writable` nya :\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n*!*\n  writable: false\n*/!*\n});\n\n*!*\nuser.name = \"Pete\"; // Error: Cannot assign to read only property 'name'\n*/!*\n```\nSekarang tidak ada yang dapat mengubah nama dari user kita, kecuali mereka menetapkan `defineProperty`nya sendiri untuk menimpa konfigurasi kita.\n\n\n```smart header=\"Errors appear only in strict mode\"\nPada mode non-strict, tidak ada eror yang terjadi ketika menulis pada properti non-writable dan sejenisnya. tapi operasi itu tetap tidak akan berhasil. Aksi pelanggaran flag hanya saja diabaikan pada mode non-strict.\n```\n\nBerikut contoh kasus yang sama, tapi properti itu dibuat dari awal:\n\n```js run\nlet user = { };\n\nObject.defineProperty(user, \"name\", {\n*!*\n  value: \"John\",\n  // untuk properti baru kita butuh untuk memberi tau apa saja yang benar/true\n  enumerable: true,\n  configurable: true\n*/!*\n});\n\nalert(user.name); // John\nuser.name = \"Pete\"; // Error\n```\n\n## Non-enumerable\n\nSekarang mari kita tambahkan `toString` yang sudah disesuaikan pada `user`.\n\nSecara normal, bawaan `toString` untuk objek merupakan non-enumerable (tidak bisa diiterasi), itu tidak akan muncul pada `for..in`. Tapi apabila kita tambahkan`toString` yang kita buat sendiri, maka secara default akan muncul pada `for..in`, contohnya seperti ini:\n\n```js run\nlet user = {\n  name: \"John\",\n  toString() {\n    return this.name;\n  }\n};\n\n// secara default, kedua properti tersebut akan terdaftar:\nfor (let key in user) alert(key); // name, toString\n```\nJika kita tidak menyukainya, maka kita bisa mengatur menjadi `enumerable:false`. yang kemudian itu tidak akan tampil pada `for..in` loop, seperti pada bawaannya: \n\n\n```js run\nlet user = {\n  name: \"John\",\n  toString() {\n    return this.name;\n  }\n};\n\nObject.defineProperty(user, \"toString\", {\n*!*\n  enumerable: false\n*/!*\n});\n\n*!*\n// sekarang toString kita tidak akan muncul:\n*/!*\nfor (let key in user) alert(key); // name\n```\n\nProperti non-enumerable juga tidak termasuk dari `Object.keys`:\n\n```js\nalert(Object.keys(user)); // name\n```\n\n## Non-configurable\n\nFlag non-configurable  (`configurable:false`) terkadang sudah diatur sebelumnya untuk objek dan properti bawaan.\n\nSebuah properti non-configurable tidak bisa di hapus.\n\nSebagai contoh, `Math.PI` adalah non-writable, non-enumerable and non-configurable:\n\n```js run\nlet descriptor = Object.getOwnPropertyDescriptor(Math, 'PI');\n\nalert( JSON.stringify(descriptor, null, 2 ) );\n/*\n{\n  \"value\": 3.141592653589793,\n  \"writable\": false,\n  \"enumerable\": false,\n  \"configurable\": false\n}\n*/\n```\nJadi, seorang programer tidak akan bisa mengganti nilai dari sebuah `Math.PI` atau juga menimpanya.\n\n```js run\nMath.PI = 3; // Error\n\n// menghapus Math.PI juga tidak akan bekerja\n```\nMembuat sebuah properti non-configurable adalah jalan satu arah. kita tidak bisa mengubahnya kembali dengan `defineProperty`.\n\nTepatnya, non-configurable memberlakukan beberapa pembatasan pada `defineProperty`:\n1. tidak bisa mengubah flag `configurable` .\n2. tidak bisa mengubah flag `enumerable` .\n3. tidak bisa mengubah `writable: false` menjadi `true` (kebalikannya masih bisa bekerja).\n4. tidak bisa mengubah `get/set` untuk sebuah properti aksesor (tapi bisa menetapkannya jika kosong).\n\nDisini kita membuat `user.name` menjadi sebuah konstant \"yang selamanya tersegel\":\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n  configurable: false\n});\n\nuser.name = \"Pete\"; // works fine\ndelete user.name; // Error\n```\n\nAnd here we make `user.name` a \"forever sealed\" constant:\n\n```js run\nlet user = {\n  name: \"John\"\n};\n\nObject.defineProperty(user, \"name\", {\n  writable: false,\n  configurable: false\n});\n\n*!*\n// tidak bisa mengubah user.name atau flag nya\n// semua dibawah ini tidak akan bekerja:\n//   user.name = \"Pete\"\n//   delete user.name\n//   Object.defineProperty(user, \"name\", { value: \"Pete\" })\nObject.defineProperty(user, \"name\", {writable: true}); // Error\n*/!*\n```\n\n```smart header=\"\\\"Non-configurable\\\" doesn't mean \\\"non-writable\\\"\"\nCatatan pengecualian: sebuah nilai dari non-configurable, tapi writable properti masih bisa diubah.\n\nIde dari `configurable: false` adalah untuk mencegah perubahan properti flag dan penghapusannya, bukan perubahan dalam nilainya.\n```\n\n## Object.defineProperties\n\nAda sebuah method [Object.defineProperties(obj, descriptors)](mdn:js/Object/defineProperties) yang memperbolehkan untuk mendefinisikan banyak properti pada satu waktu.\n\nSintaksnya adalah:\n\n```js\nObject.defineProperties(obj, {\n  prop1: descriptor1,\n  prop2: descriptor2\n  // ...\n});\n```\n\nSebagai contoh:\n\n```js\nObject.defineProperties(user, {\n  name: { value: \"John\", writable: false },\n  surname: { value: \"Smith\", writable: false },\n  // ...\n});\n```\n\nJadi, kita bisa mengatur banyak properti dalam satu waktu.\n\n## Object.getOwnPropertyDescriptors\n\nUntuk mendapat semua properti deskriptor pada satu waktu, kita dapat menggunakan sebuah method [Object.getOwnPropertyDescriptors(obj)](mdn:js/Object/getOwnPropertyDescriptors).\n\nBersamaan dengan `Object.defineProperties` itu dapat digunakan menjadi cara \"flags-aware\" untuk mengkloning sebuah objek:\n\n```js\nlet clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));\n```\n\nNormalnya ketika kita mengkloning sebuah objek, kita menggunakan penetapan nilai untuk menyalin propertinya, seperti ini:\n\n```js\nfor (let key in user) {\n  clone[key] = user[key]\n}\n```\n...Tapi itu tidak menyalin flagnya. jadi jika kita ingin sesuatu salinan \"yang lebih baik\" maka  `Object.defineProperties` lebih disarankan.\n\nPerbedaan lainnya adalah jika `for..in` mengabaikan properti simbolik, tapi `Object.getOwnPropertyDescriptors` mengembalikan *semua* properti deskriptor termasuk properti simboliknya.\n\n## menyegel sebuah objek secara global\n\nProperti deskriptor bekerja pada level invidual propertinya.\n\nDan ada juga method yang memberi batasan akses terkait *keselurhan* objek:\n\n[Object.preventExtensions(obj)](mdn:js/Object/preventExtensions)\n: Melarang penambahan pada properti baru dalam objek.\n\n[Object.seal(obj)](mdn:js/Object/seal)\n: Melarang penambahan/penghapusan dari properti. Menetapkan `configurable: false` untuk semua properti yang ada.\n\n[Object.freeze(obj)](mdn:js/Object/freeze)\n: Melarang penambahan/pengurangan/pengubahan pada properti. Menetapkan `configurable: false, writable: false` untuk semua properti yang ada.\n\nDan ada juga test untuk mereka:\n\n[Object.isExtensible(obj)](mdn:js/Object/isExtensible)\n: Mengembalikan `false` jika menambahkan properti itu dilarang, selain itu `true`.\n\n[Object.isSealed(obj)](mdn:js/Object/isSealed)\n: Mengembalikan `true` jika menambahkan/mengurangi properti itu dilarang, dan semua properti yang ada memiliki  `configurable: false`.\n\n[Object.isFrozen(obj)](mdn:js/Object/isFrozen)\n: Mengembalikan `true` jika menambahkan/mengurangi/mengubah properti itu dilarang, dan semua properti yang sekarang memiliki `configurable: false, writable: false`.\n\nMethod diatas biasanya jarang digunakan pada prakteknya.\n"
  },
  {
    "path": "1-js/07-object-properties/02-property-accessors/article.md",
    "content": "\n# Properti getter and setter\n\nTerdapat dua jenis properti objek.\n\nYang pertama adalah *properti data*. Kita telah mengetahui bagaimana cara kerja mereka. Semua properti yang kita gunakan sampai sekarang adalah properti data.\n\nYang kedua adalah properti yang bisa dibilang cukup baru. Properti itu adalah *properti aksesor*. Mereka sebenarnya adalah fungsi untuk mendapatkan dan mengatur sebuah nilai, tapi mereka mirip seperti properti biasa pada kode eksternal.\n\n## Getter dan setter\n\nProperti aksesor diwakili dengan method \"getter\" dan \"setter\". Didalam objek literal mereka dilambangkan dengan `get` dan `set`:\n\n```js\nlet obj = {\n  *!*get propName()*/!* {\n    // getter, kode dijalankan untuk mendapat obj.propName\n  },\n\n  *!*set propName(value)*/!* {\n    // setter, kode dijalankan untuk mengatur obj.propName = value\n  }\n};\n```\n\nGetter bekerja ketika `obj.propName` terbaca, sedangkan setter -- ketika variabel itu ditetapkan.\n\nSebagai contoh, kita memiliki sebuah objek `user` dengan `name` dan `surname` (nama variabel): \n\n```js\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\n```\n\nSekarang kita ingin untuk menambahkan sebuah properti `fullName`, yang berisi `\"John Smith\"`. kita tidak ingin untuk melakukan penyalinan terhadap informasi yang sudah ada, melainkan kita bisa menerapakan sebuah aksesor:\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n*!*\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  }\n*/!*\n};\n\n*!*\nalert(user.fullName); // John Smith\n*/!*\n```\n\nDari luar, properti aksesor tampak seperti variabel pada umumnya. Itulah ide dari properti aksesor. Kita tidak *memanggil* `user.fullName` melaui fungsi, Namun kita *membacanya* secara biasa: properti getter berjalan di belakang layar.\n\nSekarang, `fullName` memiliki sebuah properti getter. Jika kita mencoba untuk menetapkan value lain pada `user.fulName=`, maka akan terjadi eror:\n\n```js run\nlet user = {\n  get fullName() {\n    return `...`;\n  }\n};\n\n*!*\nuser.fullName = \"Test\"; // Error (property has only a getter)\n*/!*\n```\n\nMari kita perbaiki dengan menambahkan setter untuk `user.fullName`:\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  },\n\n*!*\n  set fullName(value) {\n    [this.name, this.surname] = value.split(\" \");\n  }\n*/!*\n};\n\n// variabel fullName dijalankan dengan value ditetapkan.  \nuser.fullName = \"Alice Cooper\";\n\nalert(user.name); // Alice\nalert(user.surname); // Cooper\n```\n\nAlhasil, kita memiliki sebuah properti virtual `fullname`. Yang bisa di baca dan diatur nilainya.\n\n## Deskriptor aksesor \n\nDeskriptor untuk properti aksesor berbeda dengan yang ada di dalam properti data.\n\nUntuk properti aksesor, tidak ada `nilai` atau `pengaturan` dalam properti aksesor, melainkan digantikan dengan fungsi `get` dan `set`. \n\nYang berarti, deskriptor aksesor mungkin memiliki:\n\n- **`get`** -- sebuah fungsi tanpa argument, yang bekerja ketika properti dibaca,\n- **`set`** -- sebuah fungsi dengan satu argumen, yang dipanggil ketika properti itu ditetapkan,\n- **`enumerable`** -- sama seperti pada properti data,\n- **`configurable`** -- sama seperti pada properti data.\n\nSebagai contoh, untuk membuat sebuah aksesor `fullName` dengan `defineProperty`, kita dapat membawa sebuah deskriptor dengan `get` dan `set`:\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\"\n};\n\n*!*\nObject.defineProperty(user, 'fullName', {\n  get() {\n    return `${this.name} ${this.surname}`;\n  },\n\n  set(value) {\n    [this.name, this.surname] = value.split(\" \");\n  }\n*/!*\n});\n\nalert(user.fullName); // John Smith\n\nfor(let key in user) alert(key); // name, surname\n```\n\nPerlu diperhatikan bahwa sebuah properti bisa jadi adalah sebuah properti aksesor(memiliki method `get/set`) atau sebuah properti data(hanya memiliki sebuah `nilai`), namun tidak keduanya.\n\nJika kita mencoba untuk menyediakan `get` dan `value` pada satu deskriptor yang sama, maka akan terjadi eror.\n\n```js run\n*!*\n// Error: Invalid property descriptor.\n*/!*\nObject.defineProperty({}, 'prop', {\n  get() {\n    return 1\n  },\n\n  value: 2\n});\n```\n\n## getter/setter yang lebih baik\n\nGetter/setter dapat digunakan sebagai wrapper pada properti `asli`(bukan aksesor) untuk mendapatkan akses kontrol lebih terkait pengoperasian dengan mereka.\n\nSebagai contoh, jika kita ingin untuk melarang penamaan yang terlalu singkat untuk `user`, kita dapat memiliki sebuah setter `name` dan menjaga nilainya pada properti yang terpisah `_name`:\n\n```js run\nlet user = {\n  get name() {\n    return this._name;\n  },\n\n  set name(value) {\n    if (value.length < 4) {\n      alert(\"Name is too short, need at least 4 characters\");\n      return;\n    }\n    this._name = value;\n  }\n};\n\nuser.name = \"Pete\";\nalert(user.name); // Pete\n\nuser.name = \"\"; // Name is too short...\n```\n\nJadi, variabel name tersebut disimpan pada properti `_name`, yang aksesnya dapat melalui getter dan setter.\n\nSecara teknis, kode eksternal bisa aja mengakses variabel nama secara langsung dengan menggunakan `user._name`. Tapi sudah menjadi rahasia umum bahwa properti yang diawali dengan underscore `\"_\"` adalah internal variabel yang seharusnya tidak boleh diakses dari luar.\n\n\n## Penggunaan kompabilitas\n\nSalah satu kegunaan besar properti aksesor adalah mereka memperbolehkan kita untuk mengontrol properti data `biasa` untuk menggantinya pada suatu waktu, dengan sebuah setter dan getter, serta mengubah perilakunya.\n\nBayangkan kita mulai dengan implementasi objek user menggunakan properti `name` dan `age`.\n\n```js\nfunction User(name, age) {\n  this.name = name;\n  this.age = age;\n}\n\nlet john = new User(\"John\", 25);\n\nalert( john.age ); // 25\n```\n\n...Tapi cepat atau lambat, sesuatu mungkin berubah. Alih-alih menggunakan `age` kita mungkin memutuskan untuk menyimpan `birthday`, karena mungkin itu lebih tepat dan sesuai:\n\n```js\nfunction User(name, birthday) {\n  this.name = name;\n  this.birthday = birthday;\n}\n\nlet john = new User(\"John\", new Date(1992, 6, 1));\n```\n\nSekarang apa yang akan kita lakukan terhadap kode lama yang masih menggunakan properti`age`?\n\nKita dapat mencoba untuk mencarinya disemua tempat dan memperbaiki nya, tapi itu akan memakan waktu yang lama dan susah jika kode itu digunakan oleh banyak orang. selain itu, properti `age` adalah sesuatu yang bagus dimiliki oleh user, kan ?\n\nTetaplah menjaganya.\n\nMenambahkan sebuah getter pada `age` menyelesaikan permasalahan.\n\n```js run no-beautify\nfunction User(name, birthday) {\n  this.name = name;\n  this.birthday = birthday;\n\n*!*\n  // variabel age dihitung berdasarkan tanggal sekarang dan tanggal lahirnya\n  Object.defineProperty(this, \"age\", {\n    get() {\n      let todayYear = new Date().getFullYear();\n      return todayYear - this.birthday.getFullYear();\n    }\n  });\n*/!*\n}\n\nlet john = new User(\"John\", new Date(1992, 6, 1));\n\nalert( john.birthday ); // properti birthday tersedia \nalert( john.age );      // ...begitu juga dengan age\n```\n\nSekarang kode yang lama bisa bekerja dan kita memiliki tambahan properti yang bagus.\n"
  },
  {
    "path": "1-js/07-object-properties/index.md",
    "content": "# Object properties configuration\n\nIn this section we return to objects and study their properties even more in-depth.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/solution.md",
    "content": "\n1. `true`, diambil dari `rabbit`.\n2. `null`, diambil dari `animal`.\n3. `undefined`, propertinya sudah tidak ada.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/1-property-after-delete/task.md",
    "content": "Nilai: 5\n\n---\n\n# Bekerja dengan prototype\n\nIni adalah kode yang membuat sepasang objek, lalu dimodifikasi.\n\nNilai manakan yang akan muncul?\n\n```js\nlet animal = {\n  jumps: null\n};\nlet rabbit = {\n  __proto__: animal,\n  jumps: true\n};\n\nalert( rabbit.jumps ); // ? (1)\n\ndelete rabbit.jumps;\n\nalert( rabbit.jumps ); // ? (2)\n\ndelete animal.jumps;\n\nalert( rabbit.jumps ); // ? (3)\n```\n\nSeharusnya ada 3 jawaban.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/solution.md",
    "content": "\n1. Let's add `__proto__`:\n\n    ```js run\n    let head = {\n      glasses: 1\n    };\n\n    let table = {\n      pen: 3,\n      __proto__: head\n    };\n\n    let bed = {\n      sheet: 1,\n      pillow: 2,\n      __proto__: table\n    };\n\n    let pockets = {\n      money: 2000,\n      __proto__: bed\n    };\n\n    alert( pockets.pen ); // 3\n    alert( bed.glasses ); // 1\n    alert( table.money ); // undefined\n    ```\n\n2. Dalam mesin modern, kinerja yang bagus, tidak ada perbedaan apakan kita mengambil properti dari sebuah objek atau dari *prototype*nya. Mesinnya akan ingat darimana mengambil propertinya dan menggunakannya kembali pada request selanjutnya.\n\n    Contoh, untuk `pockets.glasses` mereka ingat dimana `glasses` ditemukan (dalam `head`), dan pencarian selanjutnya akan dicari ditempat yang sama. Mesinnya juga cukup pintar untuk memperbaharui *cache internal* jika sesuatu berubah, jadi optimasinya akan aman.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/2-search-algorithm/task.md",
    "content": "Nilai: 5\n\n---\n\n# Algoritma pencarian\n\nTugasnya memiliki dua bagian.\n\nDiberikan objek-objek berikut:\n\n```js\nlet head = {\n  glasses: 1\n};\n\nlet table = {\n  pen: 3\n};\n\nlet bed = {\n  sheet: 1,\n  pillow: 2\n};\n\nlet pockets = {\n  money: 2000\n};\n```\n\n1. Gunakan `__proto__` untuk memasukan *prototype* dengan cara yang mana membuat property yang mencari akan mengikuti *path*: `pockets` -> `bed` -> `table` -> `head`. Contoh, `pockets.pen` haruslah `3` (ditemukan di `table`), dan `bed.glasses` haruslah `1` (ditemukan didalam `head`).\n2. Jawab pertanyaan: mana yang lebih cepat didapatkan `glasses` sebagai `pockets.glasses` atau `head.glasses`? Jika diperlukan gunakanlah *benchmark*.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/solution.md",
    "content": "**Jawabannya: `rabbit`.**\n\nKarena `this` adalah sebuah objek sebelum titik, jadi `rabbit.eat()` memodifikasi `rabbit`.\n\nPencarian properti dan eksekusi adalah dua hal yang berbeda.\n\nThe method `rabbit.eat` is first found in the prototype, then executed with `this=rabbit`.\nMetode `rabbit.eat` adalah yang pertama ditemukan dalam *prototype*, lalu dieksekusi dengan `this=rabbit`.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/3-proto-and-this/task.md",
    "content": "Nilai: 5\n\n---\n\n# Dimanakah akan tertulis?\n\nKita memiliki `rabbit` mewarisi dari `animal`.\n\nJika kita memanggil `rabbit.eat()`, yang mana objeknya menerima properti `full`: `animal` atau `rabbit`?\n\n```js\nlet animal = {\n  eat() {\n    this.full = true;\n  }\n};\n\nlet rabbit = {\n  __proto__: animal\n};\n\nrabbit.eat();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/solution.md",
    "content": "Kita perhatikan baik-baik pada apa yang terjadi dalam pemanggilannya `speedy.eat(\"apple\")`.\n\n1. Metode `speedy.eat` ditemukan dalam prototype (`hamster`), lalu dieksekusi dengan `this=speedy` (objek sebelum titik).\n\n2. Lalu `this.stomach.push()` perlu menemukan properti `stomach` dan panggil `push` didalamnya. Itu akan mencati `stomach` didalam `this` (`=speedy`), tapi tidak menemukan apapun.\n\n3. Lalu akan mengikuti rantai *prototype* dan menemukan `stomach` didalam `hamster`.\n\n4. lalu akan memanggil `push` didalamnya, menambahkan makanan kedalam *stomach dari prototype*.\n\nJadi semua hamster membagi satu *stomach*!\n\nDiantara `lazy.stomach.push(...)` dan `speedy.stomach.push()`. properti `stomach` ditemukan didalam *prototype* (sebagaimana tidak didalam objeknya sendiri) , lalu datanya akan dimasukan.\n\nPerhatikan bahwa hal tersebut tidak akan terjadi pada *assignment* sederhana `this.stomach=`:\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n*!*\n    // masukan this.stomach daripada this.stomach.push\n    this.stomach = [food];\n*/!*\n  }\n};\n\nlet speedy = {\n   __proto__: hamster\n};\n\nlet lazy = {\n  __proto__: hamster\n};\n\n// Speedy one menemukan makanannya\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// Perut Lazy one kosong\nalert( lazy.stomach ); // <nothing>\n```\n\nSekarang semuanya berjalan dengan baik, karena `this.stomach=` tidak melakukan pencarian `stomach`. Nilainya ditulis langsung kedalam objek `this`.\n\nKita juga bisa benar-benar menghindar dari masalah dengan memastikan bahwa setiak hamster memiliki perut mereka masing-masing:\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n    this.stomach.push(food);\n  }\n};\n\nlet speedy = {\n  __proto__: hamster,\n*!*\n  stomach: []\n*/!*\n};\n\nlet lazy = {\n  __proto__: hamster,\n*!*\n  stomach: []\n*/!*\n};\n\n// Speedy one menemukan makanan\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// Perut Lazy one kosong\nalert( lazy.stomach ); // <nothing>\n```\n\nSebagai solusi umum, seluruh properti yang dideskripsikan dari objek tertentu, seperti `stomach` diatas, seharusnya ditulis kedalam objeknya. Untuk menghindari masalah seperti itu.\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/4-hamster-proto/task.md",
    "content": "Nilai: 5\n\n---\n\n# Kenapa kedua hamster kenyang?\n\nKita memiliki dua hamster: `speedy` dan `lazy` yang mewarisi objek `hamster`.\n\nKetika kita memberi makan salah satunya, yang satunya lagi akan ikut kenyang. Kenapa? Bagaimana cara memperbaikinya?\n\n\n```js run\nlet hamster = {\n  stomach: [],\n\n  eat(food) {\n    this.stomach.push(food);\n  }\n};\n\nlet speedy = {\n  __proto__: hamster\n};\n\nlet lazy = {\n  __proto__: hamster\n};\n\n// Yang satu ini menemukan makanan\nspeedy.eat(\"apple\");\nalert( speedy.stomach ); // apple\n\n// Yang ini juga memilikinya, kenapa? perbaikilah.\nalert( lazy.stomach ); // apple\n```\n\n"
  },
  {
    "path": "1-js/08-prototypes/01-prototype-inheritance/article.md",
    "content": "# Pewarisan *Prototype* (*Prototypal Inheritance*)\n\nDalam *programming*, terkadang kita ingin mengambil sesuatu lalu dikembangkan lagi.\n\nContoh, kita memiliki objek `user` lengkap dengan properti dan metodenya, dan kita ingin membuat `admin` dan `guest` sebagai varian yang sedikit diubah dari objek `user`. Kita ingin menggunakan apa yang dimiliki oleh `user`, bukan menyalin ataupun meimplementasikan ulang metode-metodenya, akan tetapi menciptakan objek baru diatasnya.\n\n*Pewarisan Prototype* adalah fitur yang bisa membantu untuk melakukan hal itu.\n\n## [[Prototype]]\n\nDidalam Javascript, objek memiliki properti tersembunyi yang spesial `[[Prototype]]` (seperti yang dinamakan didalam spesifikasinya), yang mana dapat mereferensi pada `null` atau mereferensi pada objek lainnya. Objek itu disebut dengan *prototype*:\n\n![prototype](object-prototype-empty.svg)\n\nSaat kita membaca properti dari `objek`, dan properti itu hilang, JavaScript secara otomatis mengambilnya dari prototipe. Dalam pemrograman, ini disebut \"pewarisan prototipe\". Dan segera kita akan mempelajari banyak contoh pewarisan tersebut, serta fitur bahasa yang lebih keren yang dibangun di atasnya.\n\nProperti yang dimiliki `[[Prototype]]` bersifat internal dan tersembunyi, tapi ada banyak cara untuk melihat properti tersebut.\n\nSalah satunya adalah menggunakan nama spesial `__proto__`, seperti:\n\n```js run\nlet animal = {\n  eats: true\n};\nlet rabbit = {\n  jumps: true\n};\n\n*!*\nrabbit.__proto__ = animal; // sets rabbit.[[Prototype]] = animal\n*/!*\n```\n\nSekarang jika kita ingin membaca properti dari `rabbit`, dan ternyada tidak ada, Javascript akan mengambilnya dari `animal`.\n\nContoh:\n\n```js\nlet animal = {\n  eats: true\n};\nlet rabbit = {\n  jumps: true\n};\n\n*!*\nrabbit.__proto__ = animal; // (*)\n*/!*\n\n// sekarang kita bisa menggunakan kedua propertinya didalam *rabbit*:\n*!*\nalert( rabbit.eats ); // true (**)\n*/!*\nalert( rabbit.jumps ); // true\n```\n\nPada baris `(*)` menyetel `animal` untuk menjadi prototype dari `rabbit`.\n\nLalu, ketika `alert` mencoba untuk membaca properti `rabbit.eats` `(**)`, ternyata `rabbit` tidak memiliki propertinya, maka Javascript mengikuti referensi `[[Prototype]]`nya dan menemukan `animal` (mencari dari bawah ke atas):\n\n![](proto-animal-rabbit.svg)\n\nDisini kita bisa berkata bahwa \"`animal` adalah prototype dari `rabbit`\" atau \"`rabbit` secara prototype mewarisi dari `animal`\".\n\nJadi jika `animal` memiliki banyak properti dan metode yang berguna, maka properti dan metode tersebut secara otomatis akan tersedia didalam `rabbit`. Properti tersebut dinamakan \"pewarisan\".\n\nJika kita memiliki metode didalam `animal`, maka metode tersebut dapat dipanggil didalam `rabbit`:\n\n```js run\nlet animal = {\n  eats: true,\n*!*\n  walk() {\n    alert(\"Animal walk\");\n  }\n*/!*\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\n// walk diambil dari prototype\n*!*\nrabbit.walk(); // Animal walk\n*/!*\n```\n\nMetodenya secara otomatis diambil dari *prototype*nya, seperti:\n\n![](proto-animal-rabbit-walk.svg)\n\nRantai *prototype* bisa lebih panjang:\n\n```js run\nlet animal = {\n  eats: true,\n  walk() {\n    alert(\"Animal walk\");\n  }\n};\n\nlet rabbit = {\n  jumps: true,\n*!*\n  __proto__: animal\n*/!*\n};\n\nlet longEar = {\n  earLength: 10,\n*!*\n  __proto__: rabbit\n*/!*\n};\n\n// walk diambil dari rantai prototype\nlongEar.walk(); // Animal walk\nalert(longEar.jumps); // true (dari rabbit)\n```\n\n![](proto-animal-rabbit-chain.svg)\n\nSekarang jika kita membaca sesuatu dari `longEar`, dan ternyata tidak ada, Javascript akan mencarinya didalam `rabbit`, dan lalu didalam `animal`.\n\nAkan tetapi terdapat dua batasan:\n\n1. Referensinya tidak bisa berputar (seperti lingkaran atau perulangan tak terhingga). Javascript akan mengembalikan error jika kita mencoba untuk membuat `__proto__` berputar.\n2. Nilai dari `__proto__` bisa antara sebuah objek atau `null`. Tipe lainnya akan diabaikan.\n\nDan juga tentu saja: hanya terdapat satu `[[Prototype]]`. Sebuah objek tidak bisa mewarisi dari dua objek.\n\n\n```smart header=\"`__proto__` adalah asal usul getter/setter untuk `[[Prototype]]`\"\nBiasanya kesalan *developer* pemula adalah tidak mengetahui perbedaan antara keduanya.\n\nPerlu diingat bahwa `__proto__` *tidak sama* dengan properti internal `[[Prototype]]`. Itu hanyalah *getter/setter* untuk `[[Prototype]]`. Nanti kita akan melihat situasi dimana hal itu akan digunakan, untuk sekarang kita hanya perlu tahu, kita akan terus bangun pemahaman kita tentang Javascript.\n\nProperti `__proto__` sedikit ketinggalan jaman. Properti tersebut ada karena alasan lama, pada Javascript terbaru merekomendasikan kita untuk menggunakan fungsi `Object.getPrototypeOf/Object.setPrototypeOf` daripada *prototype* get/set. Kita akan belajar tentang fungsi ini nanti.\n\nDari spesifikasinya, `__proto__` telah didukung oleh banyak *browser*. Faktanya, seluruh lingkungan termasuk dibagian *server* juga mendukung `__proto__`, jadi kita aman untuk menggunakannya.\n\nKarena notasi `__proto__` sedikit lebih jelas, kita akan menggunakannya didalam contoh.\n```\n\n## Menulis tanpa menggunakan *prototype*\n\n*Prototype* hanya digunakan untuk membaca properti.\n\nOperasi menulis / menghapus bekerja secara langsung dengan objeknya.\n\nDidalam contoh dibawah, kita memasukan metode `walk` kedalam `rabbit`:\n\n```js run\nlet animal = {\n  eats: true,\n  walk() {\n    /* metode ini tidak akan digunakan oleh rabbit */\n  }\n};\n\nlet rabbit = {\n  __proto__: animal\n};\n\n*!*\nrabbit.walk = function() {\n  alert(\"Rabbit! Bounce-bounce!\");\n};\n*/!*\n\nrabbit.walk(); // Rabbit! Bounce-bounce!\n```\n\nMulai sekarang, pemanggilan `rabbit.walk()` akan menemukan metodenya secara langsung didalam objek dan langsung dieksekusi tanpa menggunakan *prototype*:\n\n![](proto-animal-rabbit-walk-2.svg)\n\nProperti pengakses adalah pengecualian, sebagaimana memasukan nilai dipegang oleh fungsi *setter*. Jadi menulis properti seperti itu sebenarnya sama dengan memanggil sebuah fungsi.\n\nUntuk alasan itu `admin.fullName` akan bekerja dengan benar pada contoh dibawah:\n\n```js run\nlet user = {\n  name: \"John\",\n  surname: \"Smith\",\n\n  set fullName(value) {\n    [this.name, this.surname] = value.split(\" \");\n  },\n\n  get fullName() {\n    return `${this.name} ${this.surname}`;\n  }\n};\n\nlet admin = {\n  __proto__: user,\n  isAdmin: true\n};\n\nalert(admin.fullName); // John Smith (*)\n\n// memicu setter!\nadmin.fullName = \"Alice Cooper\"; // (**)\n\nalert(admin.fullName); // Alice Cooper, state dari admin diubah\nalert(user.fullName); // John Smith, state dari user dilindungi / *protected*\n```\n\nDisini pada baris `(*)` properti `admin.fullName` memiliki *getter* didalam prototype `user`, jadi itu akan dipanggil. Pada baris `(**)` properti memiliki *setter* didalam *prototype*, jadi itu dipanggil.\n\n## Nilai dari \"this\"\n\nSebuah pertanyaan menarik mungkin muncul didalam contoh diatas: apa nilai dari `this` didalam `set fullName(value)`? Dimanakah properti dari `this.name` dan `this.surname` ditulis: kedalam `user` atau `admin`?\n\nJawabannya sederhana: `this` sama sekali tidak terkena efek oleh *prototype*.\n\n**Tidak peduli dimana metodenya ditemukan: didalam objek atau didalam *prototype*nya. Dalam pemanggilan metode, `this` adalah objeknya sebelum titik.**\n\nJadi, pemanggilan *setter* `admin.fullName=` menggunakan `admin` sebagai `this` dan bukan `user`.\n\nItu sebenarnya adalah sebuah hal yang sangat penting, karena kita mungkin memiliki objek yang besar dengan banyak metode, dan memiliki objek yang mewarisinya. Dan ketika pewarisan objek berjalan metode yang diwariskan, mereka hanya akan memodifikasi bagian / *state* mereka sendiri, bukan bagian dari objek besarnya.\n\nContoh, disini `animal` merepresentasikan sebuah \"method storage (penyimpanan metode)\", dan `rabbit` menggunakannya.\n\nPemanggilan `rabbit.sleep()` menyetel `this.isSleeping` didalam objek `rabbit`:\n\n```js run\n// animal memiliki metode\nlet animal = {\n  walk() {\n    if (!this.isSleeping) {\n      alert(`I walk`);\n    }\n  },\n  sleep() {\n    this.isSleeping = true;\n  }\n};\n\nlet rabbit = {\n  name: \"White Rabbit\",\n  __proto__: animal\n};\n\n// memodifikasi rabbit.isSleeping\nrabbit.sleep();\n\nalert(rabbit.isSleeping); // true\nalert(animal.isSleeping); // undefined (no such property in the prototype / tidak ada property seperti itu didalam prototype)\n```\n\nHasilnya:\n\n![](proto-animal-rabbit-walk-3.svg)\n\nJika kita memiliki objek lainnya, seperti `bird`, `snake`, dll., Mewarisi dari `animal`, mereka juga akan memiliki kases kepada metode dari `animal`. Tapi `this` didalam setiap pemanggilan metode adalah objeknya itu sendiri, mengevaluasi pada saat pemanggilan (sebelum titik), bukan `animal`. Jadi ketika kita menulis data kedalam `this`, itu akan tersimpan kedalam objeknya.\n\nSebagai hasilnya, metodenya dibagi bersama, tapi *state* dari objeknya tidak.\n\n## Perulangan for..in\n\nPerulangan `for..in` mengiterasi properti yang diwariskan juga.\n\nContoh: \n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\n*!*\n// Object.keys hanya mengembalikan kunci / keys miliknya sendiri\nalert(Object.keys(rabbit)); // jumps\n*/!*\n\n*!*\n// perulangan for..in mengiterasi kunci milik sendiri dan kunci yang diwariskan\nfor(let prop in rabbit) alert(prop); // jumps, lalu eats\n*/!*\n```\n\nJika itu bukanlah hal yang kita inginkanm dan kita ingin untuk mengecualikan properti warisan, terdapat metode bawaan [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): yang mengembalikan `true` jika `obj` memiliki properti bernama `key` (bukan properti warisan).\n\nJadi kita bisa memisahkan properti warisan (atau melakukan sesuatu dengan properti warisan itu):\n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = {\n  jumps: true,\n  __proto__: animal\n};\n\nfor(let prop in rabbit) {\n  let isOwn = rabbit.hasOwnProperty(prop);\n\n  if (isOwn) {\n    alert(`Our: ${prop}`); // Our: jumps\n  } else {\n    alert(`Inherited: ${prop}`); // Inherited: eats\n  }\n}\n```\n\nDisini kita memiliki rantai pewarisan: `rabbit` mewarisi dari `animal`, pewarisan itu dari `Object.prototype` (karena `animal` adalah objek literal `{...}`, jadi itu terjadi secara otomatis), dan lalu `null` diatasnya:\n\n![](rabbit-animal-object.svg)\n\nCatat bahwa ada satu hal lucu. darimanakah `rabbit.hasOwnProperty` datang? Kita tidak membuatnya. Lihat rantainya dan kita bisa melihat metodenya disediakan oleh `Object.prototype.hasOwnProperty`. Dengan kata lain, itu diwariskan.\n\n...Tapi kenapa `hasOwnProperty` tidak muncul didalam perulangan `for..in` seperti `eats` dan `jumps`, jika `for..in` adalah properti yang diwariskan?\n\nJawabanya sederhana: properti tersebut tidak dapat terhitung(*enumerable*). Sama seperti properti lainnya dari `Object.prototype`, yang mana memiliki tanda `enumerable:false`. Dan `for..in` hanya akan menampilkan properti yang dapat dihitung (*enumerable*). Itulah kenapa properti `Object.prototype`tidak terlihat.\n\n```smart header=\"Hampir semua metode key/value mengabaikan properti warisan\"\nHampir semua metode key/value, seperti `Object.keys`, `Object.values` dan lainnya mengabaikan properti warisan.\n\nMereka hanya akan beroperasi pada objeknya sendiri. Properti dari *prototype* *tidak* akan dihitung.\n```\n\n## Ringkasan\n\n- Dalam Javascript, seluruh objek memiliki `[[Prototype]]` tersembunyi yang mana bisa objek atau `null`.\n- Kita bisa menggunakan `obj.__proto__` untuk mengaksesnya (selain getter/setter, terdapat cara lain, yang mana akan dibahas nanti).\n- Objek yang diferensi oleh `[[Prototype]]` dipanggil dengan sebuah \"prototype\".\n- Jika kita ingin membaca sebuah properti dari `obj` atau memanggil metode, dan ternyata tidak ada maka Javascript akan mencoba mencari didalam *prototype*nya\n- Operasi menulis/menghapus langsung bekerja didalam objeknya, mereka tidak menggunakan *prototype* (asumsikan propertinya adalah data, bukan sebuah *setter*).\n- Jika kita memanggil `obj.method()`, dan `method`nya diambil dari prototype, `this` akan mereferensi `obj`. Jadi metode selalu bekerja dengan objek yang sedang digunakannya bahkan jika objeknya adalah hasil pewarisan.\n- Perulangan `for..in` mengiterasi properti asli dan properti warisan. Semua metode key/value hanya akan bekerja pada objeknya sendiri.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/1-changing-prototype/solution.md",
    "content": "\nJawaban:\n\n1. `true`. \n\n    Memasukan ke `Rabbit.prototype` menyetel `[[Prototype]]` untuk objek baru, tapi itu tidak memberikan efek pada yang sudah ada.\n\n2. `false`. \n\n    Objek yang dimasukan dengan menggunakan referensi. Objek dari `Rabbit.prototype` bukanlah di duplikasi, itu masih tetap objek tunggal yang direferensikan dari `Rabbit.prototype` dan dari `[[Prototype]]` dari `rabbit`.\n\n    Jadi ketika kita mengubah kontennya melalui satu referensi, itu masih terlihat melalui yang lainnya.\n\n3. `true`.\n\n    Semua operasi `delete` diterapkan langsung ke objeknya. Disini `delete rabbit.eats` mencoba untuk menghapus properti `eats` dari `rabbit`, tapi itu tidak memilikinya. Jadi operasinya tidak akan menghasilkan efek apapun.\n\n4. `undefined`.\n\n    Properti `eats` dihapus dari *prototype*, itu tidak akan ada lagi.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/1-changing-prototype/task.md",
    "content": "nilai: 5\n\n---\n\n# Merubah \"prototype\"\n\nDi kode dibawah kita membuat `new Rabbit`, dan mencoba memodifikasi prototypenya.\n\nPada awalnya kita memiliki kode ini:\n\n```js run\nfunction Rabbit() {}\nRabbit.prototype = {\n  eats: true\n};\n\nlet rabbit = new Rabbit();\n\nalert( rabbit.eats ); // true\n```\n\n\n1. Kita menambah satu string (ditekankan). What will `alert` show now?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    Rabbit.prototype = {};\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n2. ...Dan jika kodenya seperti ini (diganti satu baris)?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    Rabbit.prototype.eats = false;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n3. Dan seperti ini (diganti satu baris)?\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    delete rabbit.eats;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n\n4. varian terakhir:\n\n    ```js\n    function Rabbit() {}\n    Rabbit.prototype = {\n      eats: true\n    };\n\n    let rabbit = new Rabbit();\n\n    *!*\n    delete Rabbit.prototype.eats;\n    */!*\n\n    alert( rabbit.eats ); // ?\n    ```\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/solution.md",
    "content": "Kita bisa menggunakan pendekatan jika kita yakin properti `\"constructor\"` memiliki nilai yang benar.\n\nContoh, kita tidak ingin menyentuh `\"prototype\"` bawaan, maka kode ini akan berjalan dengan semestinya:\n\n```js run\nfunction User(name) {\n  this.name = name;\n}\n\nlet user = new User('John');\nlet user2 = new user.constructor('Pete');\n\nalert( user2.name ); // Pete (bekerja!)\n```\n\nKode diatas bekerja karena `User.prototype.constructor == User`.\n\nTapi jika seseorang, menimpah `User.prototype` dan lupa untuk membuat ulang `constructor` untuk `User`, maka akan membuat kegagalan.\n\nContoh:\n\n```js run\nfunction User(name) {\n  this.name = name;\n}\n*!*\nUser.prototype = {}; // (*)\n*/!*\n\nlet user = new User('John');\nlet user2 = new user.constructor('Pete');\n\nalert( user2.name ); // undefined\n```\n\nKenapa `user2.name` menghasilan `undefined`?\n\nIni bagaimana `new user.constructor('Pete')` bekerja:\n\n1. Pertama, mencari `constructor` di `user`. Tidak ada.\n2. Kemudian mengikuti rantai prototipe. Prototipe `user` adalah `User.prototype`, dan juga tidak memiliki `constructor` (karena kita \"lupa\" untuk menyetelnya dengan benar!).\n3. Lebih jauh ke atas rantai, `User.prototype` adalah objek biasa, prototipenya adalah `Object.prototype` bawaan.\n4. Terakhir, untuk `Object.prototype` bawaan, ada `Object.prototype.constructor == Object` bawaan. Jadi itu digunakan.\n\nAkhirnya, pada akhirnya, kita memiliki `let user2 = new Object('Pete')`.\n\nMungkin, bukan itu yang kita inginkan. Kami ingin membuat `Pengguna baru`, bukan `Objek baru`. Itulah hasil dari `konstruktor` yang hilang.\n\n(Untuk berjaga-jaga jika Anda penasaran, panggilan `new Object(...)` mengubah argumennya menjadi objek. Itu hal teoretis, dalam praktiknya tidak ada yang memanggil `Objek baru` dengan nilai, dan umumnya kami tidak' t gunakan `objek baru` untuk membuat objek sama sekali).\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/4-new-object-same-constructor/task.md",
    "content": "nilai: 5\n\n---\n\n# Buat sebuah objek dengan konstruktor yang sama\n\nBayangkan, kita memiliki objek yang berubah-ubah, dibuat dengan menggunakan fungsi konstruktor -- kita tidak tahu yang mana, tapi kita ingin membuat sebuah objek menggunakannya.\n\nBisakah kita melakukannya?\n\n```js\nlet obj2 = new obj.constructor();\n```\n\nBeri sebuah contoh dari menggunakan fungsi konstruktor untuk `obj` yang mana membiarkan kode seperti itu bekerja. Dan sebuah contoh yang mana membuat kodenya menjadi tidak bekerja semestinya.\n"
  },
  {
    "path": "1-js/08-prototypes/02-function-prototype/article.md",
    "content": "# F.prototype\n\nIngat ketika objek baru bisa dibuat dengan menggunakan fungsi konstruktor seperti `new F()`.\n\nJika `F.prototype` adalah sebuah objek, maka operator `new` menggunakannya untuk menyetel `[[Prototype]]` untuk objek barunya.\n\n```smart\nJavascript memiliki pewarisan *prototype* dari awal. Itu adalah salah satu fitur utama dari bahasanya.\n\nTapi dimasa lalu, hal itu tidak memiliki akses langsung. Hal yang dapat diandalkan adalah properti `\"prototype\"` dari fungsi konstruktor, yang akan dijelaskan didalam bab ini. Jadi masih banyak skrip yang masih menggunakannya.\n```\n\nCatat bahwa `F.prototype` disini berarti properti biasa yang bernama `\"prototype\"` didalam `F`. Terdengar seperti istilah \"prototype\", tapi disini kita menunjuk properti biasa dengan nama itu.\n\nContohnya:\n\n```js run\nlet animal = {\n  eats: true\n};\n\nfunction Rabbit(name) {\n  this.name = name;\n}\n\n*!*\nRabbit.prototype = animal;\n*/!*\n\nlet rabbit = new Rabbit(\"White Rabbit\"); //  rabbit.__proto__ == animal\n\nalert( rabbit.eats ); // true\n```\n\nMenyetel `Rabbit.prototype = animal` secara literal kita mengartikan: \"Ketika sebuah `new Rabbit` dibuat, itu akan memasukan `[[Prototype]]`nya ke `animal`\".\n\nHasilnya akan seperti gambar dibawah:\n\n![](proto-constructor-animal-rabbit.svg)\n\nDalam gambar, `\"prototype\"` adalah panah *horizontal*, menandakan properti *regular*, dan `[[Prototype]]` adalah panah vertikal, menandakan pewarisan `rabbit` dari `animal`.\n\n```smart header=\"`F.prototype` hanya digunakan pada `new F`\"\nProperti `F.prototype` hanya digunakan ketika `new F` dipanggil, itu memasukan `[[Prototype]]` dari objek barunya.\n\nJika, setelah pembuatan, properti `F.prototype` berubah (`F.prototype = <objek lain>`), maka objek baru yang dibuat menggunakan `new F` akan memiliki objek lain sebagai `[[Prototype]]`, tapi objek yang sudah ada akan menyimpan yang lama.\n```\n\n## F.prototype bawaan, properti konstruktor\n\nSetiap fungsi memiliki properti `\"prototype\"` bahkan jika kita tidak memberikannya.\n\n`\"prototype\"` bawaan adalah sebuah objek dengan properti `constructor` yang menunjuk balik pada fungsinya sendiri.\n\nSeperti:\n\n```js\nfunction Rabbit() {}\n\n/* default prototype\nRabbit.prototype = { constructor: Rabbit };\n*/\n```\n\n![](function-prototype-constructor.svg)\n\nKita bisa periksa:\n\n```js run\nfunction Rabbit() {}\n// secara *default*:\n// Rabbit.prototype = { constructor: Rabbit }\n\nalert( Rabbit.prototype.constructor == Rabbit ); // true\n```\n\nSecara teknis, jika kita tidak melakukan apapun, properti `constructor` akan tersedia untuk semua \"rabbits\" melalui `[[Prototype]]`:\n\n```js run\nfunction Rabbit() {}\n// secara default:\n// Rabbit.prototype = { constructor: Rabbit }\n\nlet rabbit = new Rabbit(); // mewarisi dari {constructor: Rabbit}\n\nalert(rabbit.constructor == Rabbit); // true (from prototype)\n```\n\n![](rabbit-prototype-constructor.svg)\n\nKita bisa menggunakan properti `constructor` untuk membuat objek baru menggunakan konstruktor yang sama seperti yang sudah ada.\n\nSeperti:\n\n```js run\nfunction Rabbit(name) {\n  this.name = name;\n  alert(name);\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\n*!*\nlet rabbit2 = new rabbit.constructor(\"Black Rabbit\");\n*/!*\n```\n\nHal itu akan mudak ketika kita memiliki sebuah objek, tidak tahu konstruktor yang mana yang menggunakannya (mis. ketika datang dari *library* pihak ketiga), dan kita butuh membuat satu lagi dengan bentuk yang sama.\n\nTapi mungkin hal yang paling penting tentang `\"constructor\"` adalah...\n\n**...Javascript sendiri tidak yakin dengan nilai `\"constructor\"`.**\n\nYa, terdapat nilai untuk fungsi bawaan `\"prototype\"`, tapi hanya itu. Apa yang terjadi setelahnya -- semuanya bergantung pada kita.\n\nKhususnya, jika kita mengganti seluruh prototype bawaannya, maka disana tidak akan terdapat `\"constructor\"`.\n\nContoh:\n\n```js run\nfunction Rabbit() {}\nRabbit.prototype = {\n  jumps: true\n};\n\nlet rabbit = new Rabbit();\n*!*\nalert(rabbit.constructor === Rabbit); // false\n*/!*\n```\n\nJadi, untuk menyimpan `\"constructor\"` dengan benar kita bisa memilih untuk menambahkan/menghapus properti menjadi `\"prototype\"` bawaan daripada menimpahnya dengan yang baru:\n\n```js\nfunction Rabbit() {}\n\n// Tidak menimpah Rabbit.prototype selurunya\n// hanya menambahkan\nRabbit.prototype.jumps = true\n// Rabbit.prototype.constructor bawaan diamankan\n```\n\nAtau, alternatifnya, membuat ulang properti `constructor` secara manual:\n\n```js\nRabbit.prototype = {\n  jumps: true,\n*!*\n  constructor: Rabbit\n*/!*\n};\n\n// sekarang konstruktor tidak berubah, karena kita menambahkan yang baru\n```\n\n\n## Ringkasan\n\nDidalam chapter ini kita secara jelas mendeskripsikan cara untuk menyetel `[[Prototype]]` untuk objek yang dibuat dengan menggunakan fungsi konstruktor. Nanti kita akan melihat lebih banyak alur *programming* lanjutan yang akan menggunakannya.\n\nSemuanya cukup simpel, hanya tinggal mengingat beberapa langkah untuk membuat lebih jelas:\n\n- Properti `F.prototype` (jangan keliru tentang `[[Prototype]]`) menyetel `[[Prototype]]` dari objek baru ketika `new F()` dipanggil.\n- Nilai dari `F.prototype` harusnya antara sebuah objek atau `null`: nilai lainnya tidak akan bekerja.\n- Properti `\"prototype\"` hanya memiliki efek spesial ketika menyetel fungsi konstruktor, dan dipanggil dengan `new`.\n\nDalam objek biasa `prototype` tidaklah spesial:\n```js\nlet user = {\n  name: \"John\",\n  prototype: \"Bla-bla\" // tidak ada yang spesial disini\n};\n```\n\nSecara teknis semua fungsi memiliki `F.prototype = { constructor: F }`, jadi kita bisa mendapatkan konstruktor dari sebuah objek dengan mengakses properti `\"constructor\"` miliknya sendiri.\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/solution.md",
    "content": "\n\n```js run\nFunction.prototype.defer = function(ms) {\n  setTimeout(this, ms);\n};\n\nfunction f() {\n  alert(\"Hello!\");\n}\n\nf.defer(1000); // menampilkan \"Hello!\" setelah 1 detik\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/1-defer-to-prototype/task.md",
    "content": "nilai: 5\n\n---\n\n# Menambahkan metode \"f.defer(ms)\" ke fungsi\n\nTambahkan kepada *prototype* dari semua fungsi metode `defer(ms)`, yang menjalankan fungsinya setelah milidetik `ms`.\n\nSetelah kamu melakukannya, kodenya harus berjalan:\n\n```js\nfunction f() {\n  alert(\"Hello!\");\n}\n\nf.defer(1000); // menampilkan \"Hello!\" setelah 1 detik\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/solution.md",
    "content": "\n\n```js run\nFunction.prototype.defer = function(ms) {\n  let f = this;\n  return function(...args) {\n    setTimeout(() => f.apply(this, args), ms);\n  }\n};\n\n// check it\nfunction f(a, b) {\n  alert( a + b );\n}\n\nf.defer(1000)(1, 2); // menampilkan 3 setelah 1 detik\n```\n\nIngatlah: kita menggunakan `this` didalam `f.apply` untuk membuat dekorasi kita bekerja untuk metode objek.\n\nJadi jika pembungkus fungsinya dipanggil sebagai metode objek, maka `this` diberikan kepada metode asli `f`.\n\n```js run\nFunction.prototype.defer = function(ms) {\n  let f = this;\n  return function(...args) {\n    setTimeout(() => f.apply(this, args), ms);\n  }\n};\n\nlet user = {\n  name: \"John\",\n  sayHi() {\n    alert(this.name);\n  }\n}\n\nuser.sayHi = user.sayHi.defer(1000);\n\nuser.sayHi();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/2-defer-to-prototype-extended/task.md",
    "content": "nilai: 4\n\n---\n\n# Menambah dekorasi \"defer()\" ke fungsi.\n\nTambahkan prototype dari semua fungsi metode` defer(ms)`, yang mengembalikan pembungkus, menunda pemanggilan dengan `ms` milidetik.\n\nContoh:\n\n```js\nfunction f(a, b) {\n  alert( a + b );\n}\n\nf.defer(1000)(1, 2); // tampilkan 3 setelah 1 detik\n```\n\nPerhatikan bahwa argumennya harus diberikan ke fungsi aslinya.\n"
  },
  {
    "path": "1-js/08-prototypes/03-native-prototypes/article.md",
    "content": "# *Prototype* asli\n\nProperti `\"prototype\"` adalah properti yang banyak digunakan oleh Javascript itu sendiri. Semua konstruktor fungsi menggunakannya.\n\nPertama kita akan melihat lebih lengkapnya dan bagaimana cara menggunakannya untuk menambah kemampuan dari objek-objek bawaan.\n\n## Object.prototype\n\nkatakan kita mengeluarkan sebuah objek kosong:\n\n```js run\nlet obj = {};\nalert( obj ); // \"[object Object]\" ?\n```\n\nDimanakah kode yang menghasilkan *string* `\"[object Object]\"`? Itu adalah metode bawaan `toString`, tapi diamanakah itu berara? `obj` tidak berisi apapun!\n\n...Tapi notasi pendek dari `obj = {}` sama seperti `obj = new Object()`, dimana `Object` adalah fungsi konstruktor objek bawaan, dengan properti `prototype`nya sendiri yang mereferensi sebuah objek besar `toString` dan metode lainnya.\n\nInilah yang terjadi:\n\n![](object-prototype.svg)\n\nKetika `new Object()` dipanggil (atau sebuah objek literal `{...}` dibuat), `[[Prototype]]`nya disetel ke `Object.prototype` mengikuti aturan yang telah kita bahas di bab sebelumnya:\n\n![](object-prototype-1.svg)\n\nJadi ketika `obj.toString()` dipanggil metodenya dibawa dari `Object.prototype`.\n\nKita bisa periksa seperti ini:\n\n```js run\nlet obj = {};\n\nalert(obj.__proto__ === Object.prototype); // true\n\nalert(obj.toString === obj.__proto__.toString); //true\nalert(obj.toString === Object.prototype.toString); //true\n```\n\nIngat bahwa disana sudah tidak ada lagi `[[Prototype]]` didalam rantai diatas `Object.prototype`:\n\n```js run\nalert(Object.prototype.__proto__); // null\n```\n\n## *prototype* bawaan lainnya\n\nObjek bawaan lainnya seperti `Array`, `Date`, `Function` dan lainnya juga tetap menyimpan metode didalam *prototype*.\n\nContoh, ketika kita membuat sebuah *array* `[1, 2, 3]`, konstruktor bawaan `new Array()` digunakan secara internal. Jadi `Array.prototype` menjadi *prototype* miliknya dan menyediakan metode-metode. Itu akan membuatnya menjadi efisien dalam penggunaan memori.\n\nSebagaimana spesifikasinya, semua *prototype* bawaan memiliki `Object.prototype` diatasnya. Itulah kenapa beberapa orang berkata bahwa \"semuanya diwarisi dari objek\".\n\nIni adalah gambar keseluruhan (memasangkan 3 fungsi):\n\n![](native-prototypes-classes.svg)\n\nSekarang kita cek *prototype*nya secara manual:\n\n```js run\nlet arr = [1, 2, 3];\n\n// apakah diwarisi dari Array.prototype?\nalert( arr.__proto__ === Array.prototype ); // true\n\n// maka dari Object.prototype?\nalert( arr.__proto__.__proto__ === Object.prototype ); // true\n\n// dan null diatasnya.\nalert( arr.__proto__.__proto__.__proto__ ); // null\n```\n\nBeberapa metode didalam *prototype* mungkin tumpang tindih, contoh, `Array.prototype` memiliki `toString`nya sendiri yang menyusun elemen yang dipisahkan dengan koma:\n\n```js run\nlet arr = [1, 2, 3]\nalert(arr); // 1,2,3 <-- hasil dari Array.prototype.toString\n```\n\nSeperti yang telah kita lihat, `Object.prototype` memiliki `toString` juga, tapi `Array.prototype` lebih dekat dengan rantainya, jadi varian dari *array* mungkin akan digunakan.\n\n\n![](native-prototypes-array-tostring.svg)\n\n\n\nDialam alat *browser* seperti *Chrome Developer Conolse* juga menunjukan pewarisannya (`console.dir` mungkin butuh untuk digunakan seperti objek bawaan):\n\n![](console_dir_array.png)\n\nObjek bawaan lainnya juga mungkin bekerja mirip seperti itu. Bahkan fungsi -- mereka adalah objek dari konstruktor bawaan `Function`, dan metode mereka (`call`/`apply` dan lainnya) juga diambil dari `Function.prototype`. Fungsi juga memiliki `toString` mereka masing-masing.\n\n```js run\nfunction f() {}\n\nalert(f.__proto__ == Function.prototype); // true\nalert(f.__proto__.__proto__ == Object.prototype); // true, warisan dari objek\n```\n\n## Primitif-primitif\n\nHal yang paling rumit terjadi dengan *string*, *number* dan *boolean*.\n\nSeperti yang kita ingat, mereka bukanlah objek. Tapi jika kita mencoba untuk mengakses propertinya, objek pembungkus sementara menggunakan konstruktor bawaan `String`, `Number` dan `Boolean`. Mereka menyediakan metodenya dan menghilang.\n\nObjek-objek ini dibuat tak terlihat untuk kita dan kebanyakan mesin mengoptimalkan mereka, tapi spesifikasinya menjelaskannya juga seperti itu. Metode dari objek ini juga tinggal didalam *prototype*, tersedia sebagai `String.prototype`, `Number.prototype` dan `Boolean.prototype`.\n\n```warn header=\"Nilai `null` dan `undefined` tidak memiliki objek pembungkus\"\nNilai spesial `null` dan `undefined` memiliki pendiriannya sendiri. Mereka tidak memiliki objek pembungkus, jadi metode dan properti tidak tersedia untuk mereka. Dan juga tidak terdapat prototypenya.\n```\n\n## Changing native prototypes [#native-prototype-change]\n\nPrototipe asli bisa dimodifikasi. Contoh, jika kita menambahkan metode kepada `String.prototype`, itu akan menjadi tersedia untuk selurung *string*.\n\n```js run\nString.prototype.show = function() {\n  alert(this);\n};\n\n\"BOOM!\".show(); // BOOM!\n```\n\nSelama proses pembangunan, kita mungkin memiliki ide untuk metode bawaan baru yang kita ingin punya, dan kita mungkin tergoda untuk menambahkannya sebagai *prototype* asli. Tapi itu sebenarnya bukan ide yang bagus.\n\n```warn\nPrototype terlihat di global, jadi akan mudah membuat konflik. Jika dua library menambahkan sebuah metode `String`prototype.show`, maka salah satu dari mereka akan menimpah yang lainnya.\n\nJadi, umumnya, memodifikasi prototype asli bisa dikatakan bukan ide bagus.\n```\n\n**Didalam *programming* modern, terdapat satu kasus dimana memodifikasi *prototype* asli dapat diterima. Disebut dengan *polyfilling*.**\n\n*Polyfilling* adalah sebuah istilah untuk membuat sebuah metode pengganti yang ada didalam spesifikasi Javascript, tapi itu tidak didukung oleh mesin Javascript tertentu.\n\nKita mungkin mengimplementasi manual dan mengisi prototype bawaan dengan itu.\n\nContoh:\n\n```js run\nif (!String.prototype.repeat) { // Jika tidak terdapat metode\n  // tambahkan kedalam prototype\n\n  String.prototype.repeat = function(n) {\n    // ulangi stringnya n kali\n\n    // sebenarnya, kodenya haruslah lebih rumit dari itu\n    // (algoritma lengkapnya ada didalam spesifikasinya)\n    // bahkan sebuah polyfill tidak sempurna kadang bisa dikatakan cukup bagus\n    return new Array(n + 1).join(this);\n  };\n}\n\nalert( \"La\".repeat(3) ); // LaLaLa\n```\n\n\n## meminjam dari *prototype*\n\nDidalam bab <info:call-apply-decorators#method-borrowing> kita berbicara tentang peminjaman metode.\n\nItulah ketika kita mengambil metode dari satu objek dan menyalinnya ke objek lain.\n\nBeberapa metode dari *prototype* asli sering dipinjam.\n\nContoh, jika kita membuat objek yang mirip array, kita mungkin ingin menyalin beberapa metode `Array` darinya.\n\nE.g.\n\n```js run\nlet obj = {\n  0: \"Hello\",\n  1: \"world!\",\n  length: 2,\n};\n\n*!*\nobj.join = Array.prototype.join;\n*/!*\n\nalert( obj.join(',') ); // Hello,world!\n```\n\nContoh diatas bekerja karena algoritma internal bawaan `join` yang memperhatikan tentang indeks yang benar dan `length` dari properti. Itu tidak akan memeriksa apakah objeknya adalah array. Beberapa metode bawaan memang seperti itu.\n\nKemungkinan lainnya adalah pewarisan dari `obj.__proto__` ke `Array.prototype`, jadi seluruh metode `Array` secara otomatis tersedia didalam `obj`.\n\nTapi itu menjadi tidak mungkin jika `obj` sudah mewarisi dari objek lainnya. Ingat, kita hanya bisa mewarisi dari satu objek pada satu waktu.\n\nMeminjam metode sebenarnya cukup fleksibel, hal itu memperbolehkan kita untuk mencampur fungsionalitas dari objek yang berbeda-beda jika dibutuhkan.\n\n## Ringkasan\n\n- Seluruh objek bawaan mengikuti alur yang sama:\n    - Metode disimpan didalam prototype (`Array.prototype`, `Object.prototype`, `Date.prototype`, etc.)\n    - Objeknya sendiri hanya menyimpan data (item array, properti objek, tanggal)\n- Prototype Asli menyimpan metode didalam prototype dari objek pembungkus: `Number.prototype`, `String.prototype` dan `Boolean.prototype`. Only `undefined` dan `null` tidak memiliki objek pembungkus.\n- *Prototype* bawaan bisa dimodifikasi atau diisi ulang dengan metode baru. Tapi tidak direkomendasikan untuk mengubahnya. Hal yang diperbolehkan dalam beberapa kasus mungkun ketika kita menambahkan peraturan baru, tapi itu belum sepenuhnya didukung oleh mesin Javascript.\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/solution.md",
    "content": "\nPemanggilan metode bisa mengambil semua kunci yang terhitung menggunakan `Object.keys` dan mengeluarkan daftarnya.\n\nUntuk membuat `toString` tidak bisa dihitung, kita bisa mendefinisikannya menggunakan deskriptor properti. Sintaks dari `Object.create` membolehkan kita untuk menyediakan sebuah objek dengan deskriptor properti sebagai argumen kedua.\n\n```js run\n*!*\nlet dictionary = Object.create(null, {\n  toString: { // definisikan properti tostring\n    value() { // nilainya adalah fungsi\n      return Object.keys(this).join();\n    }\n  }\n});\n*/!*\n\ndictionary.apple = \"Apple\";\ndictionary.__proto__ = \"test\";\n\n// apple dan __proto berada didalam perulangan\nfor(let key in dictionary) {\n  alert(key); // \"apple\", lalu \"__proto__\"\n}  \n\n// properti dari daftar yang dipisahkan dengan koma oleh toString\nalert(dictionary); // \"apple,__proto__\"\n```\n\nKetika kita membuat sebuah properti menggunakan deskriptor, tandanya akan menjadi `false` secara bawaan. Jadi kode diatas, `dictionary.toString` tidak bisa dihitung.\n\nLihat bab [](info:property-descriptors) untuk review.\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/2-dictionary-tostring/task.md",
    "content": "nilai: 5\n\n---\n\n# Add toString to the dictionary\n\nTerdapat sebuah objek `dictionary`, dibuat sebagai `Object.create(null)`, untuk menyimpan pasangan `key/value`.\n\nTambahkan metode `dictionary.toString()` kedalamnya, yang harus mengembalikan daftar yang dibatasi dengan koma. `toString` milikmu haruslah tidak tampil didalam `for..in` dalam objeknya.\n\nIni adalah contohnya:\n\n```js\nlet dictionary = Object.create(null);\n\n*!*\n// metode yang ditambahkan dictionary.toString\n*/!*\n\n// tambahkan beberapa data\ndictionary.apple = \"Apple\";\ndictionary.__proto__ = \"test\"; // __proto__ adalah kunci properti biasa disini\n\n// hanya apple dan __proto__ yang berada di perulangan\nfor(let key in dictionary) {\n  alert(key); // \"apple\", lalu \"__proto__\"\n}  \n\n// toString milikmu\nalert(dictionary); // \"apple,__proto__\"\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/3-compare-calls/solution.md",
    "content": "\nPanggilan pertama memiliki `this == rabbit`, yang lainnya memiliki `this` sama dengan `Rabbit.prototype`, karena itu sebenarnya adalah objek sebelum titiknya.\n\nJadi hanya panggilan pertama yang menampilkan `Rabbit`, lainnya menampilkan `undefined`:\n\n```js run\nfunction Rabbit(name) {\n  this.name = name;\n}\nRabbit.prototype.sayHi = function() {\n  alert( this.name );\n}\n\nlet rabbit = new Rabbit(\"Rabbit\");\n\nrabbit.sayHi();                        // Rabbit\nRabbit.prototype.sayHi();              // undefined\nObject.getPrototypeOf(rabbit).sayHi(); // undefined\nrabbit.__proto__.sayHi();              // undefined\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/3-compare-calls/task.md",
    "content": "nilai: 5\n\n---\n\n# Perbedaan diantara pemanggilan\n\nKita buat sebuah objek `rabbit` baru:\n\n```js\nfunction Rabbit(name) {\n  this.name = name;\n}\nRabbit.prototype.sayHi = function() {\n  alert(this.name);\n};\n\nlet rabbit = new Rabbit(\"Rabbit\");\n```\n\nApakah panggilan-panggilan dibawah sama atau tidak?\n\n```js\nrabbit.sayHi();\nRabbit.prototype.sayHi();\nObject.getPrototypeOf(rabbit).sayHi();\nrabbit.__proto__.sayHi();\n```\n"
  },
  {
    "path": "1-js/08-prototypes/04-prototype-methods/article.md",
    "content": "\n# Prototype methods, objects without __proto__\n\nIn the first chapter of this section, we mentioned that there are modern methods to setup a prototype.\n\nThe `__proto__` is considered outdated and somewhat deprecated (in browser-only part of the JavaScript standard).\n\nThe modern methods are:\n\n- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with given `proto` as `[[Prototype]]` and optional property descriptors.\n- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj`.\n- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto`.\n\nThese should be used instead of `__proto__`.\n\nFor instance:\n\n```js run\nlet animal = {\n  eats: true\n};\n\n// create a new object with animal as a prototype\n*!*\nlet rabbit = Object.create(animal);\n*/!*\n\nalert(rabbit.eats); // true\n\n*!*\nalert(Object.getPrototypeOf(rabbit) === animal); // true\n*/!*\n\n*!*\nObject.setPrototypeOf(rabbit, {}); // change the prototype of rabbit to {}\n*/!*\n```\n\n`Object.create` has an optional second argument: property descriptors. We can provide additional properties to the new object there, like this:\n\n```js run\nlet animal = {\n  eats: true\n};\n\nlet rabbit = Object.create(animal, {\n  jumps: {\n    value: true\n  }\n});\n\nalert(rabbit.jumps); // true\n```\n\nThe descriptors are in the same format as described in the chapter <info:property-descriptors>.\n\nWe can use `Object.create` to perform an object cloning more powerful than copying properties in `for..in`:\n\n```js\nlet clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n```\n\nThis call makes a truly exact copy of `obj`, including all properties: enumerable and non-enumerable, data properties and setters/getters -- everything, and with the right `[[Prototype]]`.\n\n## Brief history\n\nIf we count all the ways to manage `[[Prototype]]`, there are a lot! Many ways to do the same thing!\n\nWhy?\n\nThat's for historical reasons.\n\n- The `\"prototype\"` property of a constructor function has worked since very ancient times.\n- Later, in the year 2012, `Object.create` appeared in the standard. It gave the ability to create objects with a given prototype, but did not provide the ability to get/set it. So browsers implemented the non-standard `__proto__` accessor that allowed the user to get/set a prototype at any time.\n- Later, in the year 2015, `Object.setPrototypeOf` and `Object.getPrototypeOf` were added to the standard, to perform the same functionality as `__proto__`. As `__proto__` was de-facto implemented everywhere, it was kind-of deprecated and made its way to the Annex B of the standard, that is: optional for non-browser environments.\n\nAs of now we have all these ways at our disposal.\n\nWhy was `__proto__` replaced by the functions `getPrototypeOf/setPrototypeOf`? That's an interesting question, requiring us to understand why `__proto__` is bad. Read on to get the answer.\n\n```warn header=\"Don't change `[[Prototype]]` on existing objects if speed matters\"\nTechnically, we can get/set `[[Prototype]]` at any time. But usually we only set it once at the object creation time and don't modify it anymore: `rabbit` inherits from `animal`, and that is not going to change.\n\nAnd JavaScript engines are highly optimized for this. Changing a prototype \"on-the-fly\" with `Object.setPrototypeOf` or `obj.__proto__=` is a very slow operation as it breaks internal optimizations for object property access operations. So avoid it unless you know what you're doing, or JavaScript speed totally doesn't matter for you.\n```\n\n## \"Very plain\" objects [#very-plain]\n\nAs we know, objects can be used as associative arrays to store key/value pairs.\n\n...But if we try to store *user-provided* keys in it (for instance, a user-entered dictionary), we can see an interesting glitch: all keys work fine except `\"__proto__\"`.\n\nCheck out the example:\n\n```js run\nlet obj = {};\n\nlet key = prompt(\"What's the key?\", \"__proto__\");\nobj[key] = \"some value\";\n\nalert(obj[key]); // [object Object], not \"some value\"!\n```\n\nHere, if the user types in `__proto__`, the assignment is ignored!\n\nThat shouldn't surprise us. The `__proto__` property is special: it must be either an object or `null`. A string can not become a prototype.\n\nBut we didn't *intend* to implement such behavior, right? We want to store key/value pairs, and the key named `\"__proto__\"` was not properly saved. So that's a bug!\n\nHere the consequences are not terrible. But in other cases we may be assigning object values, and then the prototype may indeed be changed. As a result, the execution will go wrong in totally unexpected ways.\n\nWhat's worse -- usually developers do not think about such possibility at all. That makes such bugs hard to notice and even turn them into vulnerabilities, especially when JavaScript is used on server-side.\n\nUnexpected things also may happen when assigning to `toString`, which is a function by default, and to other built-in methods.\n\nHow can we avoid this problem?\n\nFirst, we can just switch to using `Map` for storage instead of plain objects, then everything's fine.\n\nBut `Object` can also serve us well here, because language creators gave thought to that problem long ago.\n\n`__proto__` is not a property of an object, but an accessor property of `Object.prototype`:\n\n![](object-prototype-2.svg)\n\nSo, if `obj.__proto__` is read or set, the corresponding getter/setter is called from its prototype, and it gets/sets `[[Prototype]]`.\n\nAs it was said in the beginning of this tutorial section: `__proto__` is a way to access `[[Prototype]]`, it is not `[[Prototype]]` itself.\n\nNow, if we intend to use an object as an associative array and be free of such problems, we can do it with a little trick:\n\n```js run\n*!*\nlet obj = Object.create(null);\n*/!*\n\nlet key = prompt(\"What's the key?\", \"__proto__\");\nobj[key] = \"some value\";\n\nalert(obj[key]); // \"some value\"\n```\n\n`Object.create(null)` creates an empty object without a prototype (`[[Prototype]]` is `null`):\n\n![](object-prototype-null.svg)\n\nSo, there is no inherited getter/setter for `__proto__`. Now it is processed as a regular data property, so the example above works right.\n\nWe can call such objects \"very plain\" or \"pure dictionary\" objects, because they are even simpler than the regular plain object `{...}`.\n\nA downside is that such objects lack any built-in object methods, e.g. `toString`:\n\n```js run\n*!*\nlet obj = Object.create(null);\n*/!*\n\nalert(obj); // Error (no toString)\n```\n\n...But that's usually fine for associative arrays.\n\nNote that most object-related methods are `Object.something(...)`, like `Object.keys(obj)` -- they are not in the prototype, so they will keep working on such objects:\n\n\n```js run\nlet chineseDictionary = Object.create(null);\nchineseDictionary.hello = \"你好\";\nchineseDictionary.bye = \"再见\";\n\nalert(Object.keys(chineseDictionary)); // hello,bye\n```\n\n## Summary\n\nModern methods to set up and directly access the prototype are:\n\n- [Object.create(proto, [descriptors])](mdn:js/Object/create) -- creates an empty object with a given `proto` as `[[Prototype]]` (can be `null`) and optional property descriptors.\n- [Object.getPrototypeOf(obj)](mdn:js/Object/getPrototypeOf) -- returns the `[[Prototype]]` of `obj` (same as `__proto__` getter).\n- [Object.setPrototypeOf(obj, proto)](mdn:js/Object/setPrototypeOf) -- sets the `[[Prototype]]` of `obj` to `proto` (same as `__proto__` setter).\n\nThe built-in `__proto__` getter/setter is unsafe if we'd want to put user-generated keys into an object. Just because a user may enter `\"__proto__\"` as the key, and there'll be an error, with hopefully light, but generally unpredictable consequences.\n\nSo we can either use `Object.create(null)` to create a \"very plain\" object without `__proto__`, or stick to `Map` objects for that.\n\nAlso, `Object.create` provides an easy way to shallow-copy an object with all descriptors:\n\n```js\nlet clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));\n```\n\nWe also made it clear that `__proto__` is a getter/setter for `[[Prototype]]` and resides in `Object.prototype`, just like other methods.\n\nWe can create an object without a prototype by `Object.create(null)`. Such objects are used as \"pure dictionaries\", they have no issues with `\"__proto__\"` as the key.\n\nOther methods:\n\n- [Object.keys(obj)](mdn:js/Object/keys) / [Object.values(obj)](mdn:js/Object/values) / [Object.entries(obj)](mdn:js/Object/entries) -- returns an array of enumerable own string property names/values/key-value pairs.\n- [Object.getOwnPropertySymbols(obj)](mdn:js/Object/getOwnPropertySymbols) -- returns an array of all own symbolic keys.\n- [Object.getOwnPropertyNames(obj)](mdn:js/Object/getOwnPropertyNames) -- returns an array of all own string keys.\n- [Reflect.ownKeys(obj)](mdn:js/Reflect/ownKeys) -- returns an array of all own keys.\n- [obj.hasOwnProperty(key)](mdn:js/Object/hasOwnProperty): returns `true` if `obj` has its own (not inherited) key named `key`.\n\nAll methods that return object properties (like `Object.keys` and others) -- return \"own\" properties. If we want inherited ones, we can use `for..in`.\n"
  },
  {
    "path": "1-js/08-prototypes/index.md",
    "content": "# Prototypes, inheritance\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/_js.view/solution.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n\n\nlet clock = new Clock({template: 'h:m:s'});\nclock.start();\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/_js.view/source.js",
    "content": "function Clock({ template }) {\n\n  let timer;\n\n  function render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  this.stop = function() {\n    clearInterval(timer);\n  };\n\n  this.start = function() {\n    render();\n    timer = setInterval(render, 1000);\n  };\n\n}\n\nlet clock = new Clock({template: 'h:m:s'});\nclock.start();\n"
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/solution.md",
    "content": ""
  },
  {
    "path": "1-js/09-classes/01-class/1-rewrite-to-class/task.md",
    "content": "nilai: 5\n\n---\n\n# Menulis ulang ke class\n\nClass `Clock` ditulis dalam gaya fungsional. Tulis ulang dengan menggunakan sintaks \"class\".\n\nP.S Class Clock berdetak pada console, buka untuk melihatnya."
  },
  {
    "path": "1-js/09-classes/01-class/article.md",
    "content": "\n# Class basic syntax\n\n```quote author=\"Wikipedia\"\nIn object-oriented programming, a *class* is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods).\n```\n\nPada kenyataannya, kita sering membutuhkan sesuatu untuk membuat banyak objek yang memiliki kemiripan, seperti user, atau benda-benda lainnya seperti buah-buahan, binatang, kendaraan dan sesuatu yang biasanya bisa dikelompokan.\n\nSeperti yang kita ketahui dari bab <info:constructor-new>, `new function` dapat membantu kita membuatnya.\n\nTapi pada Javascript modern, ada cara pembuatan \"class\" yang lebih lanjut, Yang akan memperkenalkan fitur-fitur yang sangat berguna untuk pemograman berbasis objek / objek oriented.\n\n## Sintaks \"class\"\n\nSintaks dasarnya adalah:\n```js\nclass MyClass {\n  // class methods\n  constructor() { ... }\n  method1() { ... }\n  method2() { ... }\n  method3() { ... }\n  ...\n}\n```\n\nKemudian kita gunakan `new MyClass` untuk menciptakan sebuah objek baru dengan semua method yang sudah didaftarkan.\n\nMethod `constructor()` secara otomatis terpanggil oleh `new`, jadi kita dapat menginisialisasi sebuah objek dengannya.\n\nContohnyya: \n\n```js run\nclass User {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n  sayHi() {\n    alert(this.name);\n  }\n\n}\n\n// Cara penggunaan:\nlet user = new User(\"John\");\nuser.sayHi();\n```\n\nKetika `new user(\"john\")` dijalankan:\n1. Sebuah objek baru terbentuk.\n2. method \"constructor\" berjalan dengan argumen yang dberikan dan menetapkan `this.name` pada nya.\n\n...Lalu kita dapat memanggil method objek, seperti `user.sayHi()`.\n\n```warn header=\"Tidak boleh ada koma diantara method class\"\n\nKesalahan umum bagi pemula biasanya dengan memberikan koma diantara method class, yang nantinya akan menghasilkan sintaks eror.\n\nNotasi disini jangan disamakan dengan objek literal. Dalam class, tidak perlu ada koma.\n```\n\n## Apa itu class?\n\nJadi apa sih sebenarnnya `class` itu ? Itu bukan sebuah istilah baru dalam bahasa pemograman.\n\nMari kita uraikan dan lihat apa sebenarnya class itu. Yang nantinya akan membantu untuk memahami berbagai aspek yang lebih rumit.\n\nDi Javascript, sebuah class itu merupakan sesuatu yang mirip fungsi.\n\nYuk kita lihat disini:\n\n```js run\nclass User {\n  constructor(name) { this.name = name; }\n  sayHi() { alert(this.name); }\n}\n\n// bukti: User adalah sebuah fungsi\n*!*\nalert(typeof User); // function \n*/!*\n```\n\nApa yang konstruktor `class User {...}` sebenarnya lakukan:\n\n1. Membuat sebuah fungsi bernama `User`, yang akan menjadi hasil dari deklarasi class. Sebuah fungsi kodingan diambil dari method `constructor` (kita asumsikan kosong jika kita tidak menulis sebuah method).\n2. Simpan method-method class didalamnya, seperti `sayHi`, pada `User.prototype`.\n\nSetelah objek `new User` terbentuk, ketika kita panggil method tersebut, itu akan mengambil dari prototypenya, seperti yang dijelaskan pada bab <info:function-prototype>. Sehingga objek tersebut memiliki akses pada method class nya.\n\nKita dapat mengilustrasikan hasil dari deklrasi `class User` seperti berikut:\n\n![](class-user.svg)\n\nMari kita bedah kode tersebut:\n\n```js run\nclass User {\n  constructor(name) { this.name = name; }\n  sayHi() { alert(this.name); }\n}\n\n// class adalah sebuah fungsi\nalert(typeof User); // function\n\n// ...atau, lebih tepatnya, Method constructor\nalert(User === User.prototype.constructor); // true\n\n// Method tersebut berada pada User.prototype, contoh:\nalert(User.prototype.sayHi); // alert(this.name);\n\n// Didalamnya terdapat dua method pada prototipe\nalert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi\n```\n\n## Bukan hanya sebuah pemanis sintaks belaka\n\nTerkadang orang-orang mengatakan bahwa `class` adalah sebuah `sintaksis sugar` (sintaks yang diciptakan untuk membuat mudah pembacaan, tapi tidak ada sesuatu yang baru didalamnya), karena kita sebenarnya dapat mendeklasikan objek tanpa menggunakan sintaks `class` sama sekali.\n\n```js run\n// menjalankan Class User hanya dengan fungsi\n\n// 1. Membuat fungsi konstruktor\nfunction User(name) {\n  this.name = name;\n}\n// sebuah fungsi prototipe memiliki properti \"constructor\" secara bawaannya,\n// jadi kita tidak perlu membuatnya\n\n// 2 menambahkan method pada prototype\nUser.prototype.sayHi = function() {\n  alert(this.name);\n};\n\n// Penggunaan:\nlet user = new User(\"John\");\nuser.sayHi();\n```\n\nHasil dari pendefinisian berikut adalah sama. Jadi memang ada alasan mengapa `class` dapat dianggap sebagai pemanis sintaks untuk pendefinisian konstruktor bersamaan dengan method prototipe-nya.\n\nTetap saja, ada perbedaan penting.\n\n1. Pertama, sebuah fungsi yang dibentuk dengan `class` dilabeli oleh properti internal yang khusus `[[FunctionKind]]:\"classConstructor\"`. Jadi tidak sepenuhnya sama dengan membuatnya secara manual.\n\n    Javascript sendiri mengecek properti tersebut diberbagai tempat. sebagai contoh, tidak seperti fungsi biasa, itu harus dijalankan dengan sintaks `new`:\n\n    ```js run\n    class User {\n      constructor() {}\n    }\n\n    alert(typeof User); // function\n    User(); // Error: Class constructor User cannot be invoked without 'new'\n    ```\n\n    Selain itu, representasi string dari konstruktor class di sebagian besar mesin JavaScript dimulai dengan \"class ...\"\n\n    ```js run\n    class User {\n      constructor() {}\n    }\n\n    alert(User); // class User { ... }\n    ```\n    Terdapat perbedaan lain, kita akan segera melihatnya.\n\n2. Method Class merupakan non-enumerable(tidak bisa melakukan perhitungan dengan method ini)\n    Sebuah pendefinisian class menetapkan flag `enumerable` menjadi `false` untuk semua methods yang ada di `properti` nya.\n\n    Itu bagus, karena jika kita melakukan `for..in` pada sebuah objek, kita biasanya tidak menginginkan method bawaan class nya.\n\n3. Class selalu menggunakan `use strict`.(cek apa itu [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode))\n\nSelain itu, sintaks `class` menghadirkan banyak fitur lain yang akan kita bahas nantinya.\n\n## Class Expression\n\nSama seperti fungsi, kelas dapat didefinisikan denga ekspresi lain, diturunkan, dikembalikan, ditetapkan, dll.\n\nBerikut adalah contoh class expression:\n\n```js\nlet User = class {\n  sayHi() {\n    alert(\"Hello\");\n  }\n};\n```\n\nMirip dengan fungsi Expressions yang memiliki nama, class expression juga mungkin memiliki nama.\n\nJika sebuah class expression memiliki nama, itu hanya terlihat di dalam class tersebut:\n\n```js run\n// \"Class Expression yang memiliki nama\"\n// ((tidak ada pembahasan dalam spesifikasinya, tapi itu mirip dengan function expression yang memiliki nama)\nlet User = class *!*MyClass*/!* {\n  sayHi() {\n    alert(MyClass); // Nama MyClass hanya terlihat di dalam kelas\n  }\n};\n\nnew User().sayHi(); //berjalan, menunjukkan definisi dari MyClass\n\nalert(MyClass); // error, Nama MyClass tidak bisa dilihat di luar kelas\n```\n\nKita bahkan dapat membuat kelas secara dinamis \"sesuai permintaan\", seperti berikut:\n\n```js run\nfunction makeClass(phrase) {\n  // deklrasikan sebuah class dan kembalikan class trsebut\n  return class {\n    sayHi() {\n      alert(phrase);\n    }\n  };\n}\n\n// Membuat sebuah class baru\nlet User = makeClass(\"Hello\");\n\nnew User().sayHi(); // Hello\n```\n\n\n## Getter/setter\n\nSama seperti objek literal, class dapat menyertakan getter / setter serta properti yang telah diproses, dll.\n\nBerikut adalah sebuah contoh untuk `user.name` dengan implementasi menggunakan `get/set`: \n\n```js run\nclass User {\n\n  constructor(name) {\n    // memanggil setternya\n    this.name = name;\n  }\n\n*!*\n  get name() {\n*/!*\n    return this._name;\n  }\n\n*!*\n  set name(value) {\n*/!*\n    if (value.length < 4) {\n      alert(\"Name is too short.\");\n      return;\n    }\n    this._name = value;\n  }\n\n}\n\nlet user = new User(\"John\");\nalert(user.name); // John\n\nuser = new User(\"\"); // Name is too short.\n```\n\nSecara teknis, deklarasi kelas tersebut bekerja dengan membuat getter dan setter didalam `User.prototype`.\n\n## Nama yang telah diproses [...]\n\nBerikut adalah sebuah contoh menggunakan method pada nama yang telah diproses pada bracket`[...]`:\n\n```js run\nclass User {\n\n*!*\n  ['say' + 'Hi']() {\n*/!*\n    alert(\"Hello\");\n  }\n\n}\n\nnew User().sayHi();\n```\n\nFitur seperti itu mudah diingat, karena mirip dengan objek literal.\n\n## Class fields\n\n```warn header=\"Browser lama mungkin membutuhkan polyfill\"\nClass fields adalah tambahan terbaru pada bahasa ini..\n```\n\nSebelumnya, kelas ini hanya memiliki method.\n\n\"Class fields\" adalah sebuah sintaks yang memungkinkan untuk menambahkan properti apa pun.\n\nSebagai contoh, mari kita tambahkan properti `name` pada `class User`:\n\n```js run\nclass User {\n*!*\n  name = \"John\";\n*/!*\n\n  sayHi() {\n    alert(`Hello, ${this.name}!`);\n  }\n}\n\nnew User().sayHi(); // Hello, John!\n```\n\nJadi, kita hanya perlu menulis \"<property name> = <value>\" pada saat proses deklarasi.\n\nPerbedaan penting pada class field adalah bahwa class field tersebut diatur untuk objek individual, bukan pada `User.prototype`:\n\n```js run\nclass User {\n*!*\n  name = \"John\";\n*/!*\n}\n\nlet user = new User();\nalert(user.name); // John\nalert(User.prototype.name); // undefined\n```\n\nKita juga dapat menetapkan nilai menggunakan ekspresi yang lebih kompleks dan memanggil fungsi:\n\n```js run\nclass User {\n*!*\n  name = prompt(\"Name, please?\", \"John\");\n*/!*\n}\n\nlet user = new User();\nalert(user.name); // John\n```\n\n### Membuat bound method dengan class field\n\nSeperti yang ditunjukkan pada bab <info: bind>, fungsi di JavaScript memiliki `this` yang dinamis. Itu tergantung pada konteks pemanggilannya.\n\nJadi, jika method objek diteruskan dan dipanggil pada konteks lain, `this` tidak akan menjadi referensi ke objek itu lagi.\n\nSebagai contoh, kode dibawah ini akan menampilkan output `undefined`:\n\n```js run\nclass Button {\n  constructor(value) {\n    this.value = value;\n  }\n\n  click() {\n    alert(this.value);\n  }\n}\n\nlet button = new Button(\"hello\");\n\n*!*\nsetTimeout(button.click, 1000); // undefined\n*/!*\n```\nMasalah itu terjadi karena memanggil `this` pada konteks lain.\n\nTerdapat dua pendekatan cara untuk memperbaikinya, seperti yang telah kita diskusikan pada bab <info:bind>:\n\n1. Meneruskan sebuah fungsi-wrapper, seperti `setTimeout(() => button.click(), 1000)`.\n2. Mengikat method tersebut terhadap objek, seperti pada constructor.\n\nClass field menyediakan sintaks lain yang cukup elegan:\n\n```js run\nclass Button {\n  constructor(value) {\n    this.value = value;\n  }\n*!*\n  click = () => {\n    alert(this.value);\n  }\n*/!*\n}\n\nlet button = new Button(\"hello\");\n\nsetTimeout(button.click, 1000); // hello\n```\n\nClass field ini `click = () => {...}` dibuat pada pada basis tiap objek, ada sebuah fungsi terpisah pada tiap `Button` objek, dengan `this` di dalamnya mereferensikan objek tersebut. Kita dapat meneruskan `button.click` di mana saja, dan nilai` this` akan selalu benar.\n\nItu akan sangat berguna pada lingkungan browser, teruntuk event listener.\n\n## Kesimpulan\n\nSintaks class dasar terlihat seperti ini:\n\n```js\nclass MyClass {\n  prop = value; // property\n\n  constructor(...) { // constructor\n    // ...\n  }\n\n  method(...) {} // method\n\n  get something(...) {} // getter method\n  set something(...) {} // setter method\n\n  [Symbol.iterator]() {} // method dengan nama yang telah diproses (disini simbol)\n  // ...\n}\n```\n\n`MyClass` secara teknis adalah sebuah fungsi (yang kita sediakan sebagai` konstruktor`), sedangkan method, getter, dan setter ditulis ke `MyClass.prototype`.\n\nPada bab selanjutnya kita akan mempelajari lebih lanjut tentang class, termasuk pewarisan dan fitur lainnya.\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/1-class-constructor-error/solution.md",
    "content": "Itu karena konstruktor turunan harus memanggil `super()`.\n\nBerikut kode yang benar:\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n}\n\nclass Rabbit extends Animal {\n  constructor(name) {\n    *!*\n    super(name);\n    */!*\n    this.created = Date.now();\n  }\n}\n\n*!*\nlet rabbit = new Rabbit(\"White Rabbit\"); // sekarang oke\n*/!*\nalert(rabbit.name); // White Rabbit\n```\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/1-class-constructor-error/task.md",
    "content": "importance: 5\n\n---\n\n# Kesalahan saat membuat sebuah _instance_\n\nBerikut kode dengan `Rabbit` _extending_ `Animal`.\n\nSayangnya, objek `Rabbit` tidak dapat dibuat. Apa yang salah? Perbaiki!.\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.name = name;\n  }\n\n}\n\nclass Rabbit extends Animal {\n  constructor(name) {\n    this.name = name;\n    this.created = Date.now();\n  }\n}\n\n*!*\nlet rabbit = new Rabbit(\"White Rabbit\"); // Error: this is not defined\n*/!*\nalert(rabbit.name);\n```\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.md",
    "content": "[js src=\"solution.view/extended-clock.js\"]\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/clock.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/extended-clock.js",
    "content": "class ExtendedClock extends Clock {\n  constructor(options) {\n    super(options);\n    let { precision = 1000 } = options;\n    this.precision = precision;\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), this.precision);\n  }\n};\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<script src=\"clock.js\"></script>\n<script src=\"extended-clock.js\"></script>\n\n<script>\n  let lowResolutionClock = new ExtendedClock({\n    template: 'h:m:s',\n    precision: 10000\n  });\n\n  lowResolutionClock.start();\n</script>\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/clock.js",
    "content": "class Clock {\n  constructor({ template }) {\n    this.template = template;\n  }\n\n  render() {\n    let date = new Date();\n\n    let hours = date.getHours();\n    if (hours < 10) hours = '0' + hours;\n\n    let mins = date.getMinutes();\n    if (mins < 10) mins = '0' + mins;\n\n    let secs = date.getSeconds();\n    if (secs < 10) secs = '0' + secs;\n\n    let output = this.template\n      .replace('h', hours)\n      .replace('m', mins)\n      .replace('s', secs);\n\n    console.log(output);\n  }\n\n  stop() {\n    clearInterval(this.timer);\n  }\n\n  start() {\n    this.render();\n    this.timer = setInterval(() => this.render(), 1000);\n  }\n}\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/source.view/index.html",
    "content": "<!DOCTYPE html>\n<script src=\"clock.js\"></script>\n<script>\n  let clock = new Clock({\n    template: 'h:m:s',\n  });\n  clock.start();\n\n  /* Kelas kamu harus bekerja seperti ini: */\n\n  /*\n\n    let lowResolutionClock = new ExtendedClock({\n      template: 'h:m:s',\n      precision: 10000\n    });\n\n    lowResolutionClock.start();\n  */\n</script>\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/2-clock-class-extended/task.md",
    "content": "importance: 5\n\n---\n\n# _Extended clock_\n\nKita punya kelas `Clock`. Sampai sekarang, ini mencetak waktu setiap detik.\n\n[js src=\"source.view/clock.js\"]\n\nBuat kelas baru `ExtendedClock` yang diturunkan dari` Clock` dan tambahkan parameter `precision` -- jumlah` ms` antara \"ticks\". Seharusnya `1000` (1 detik) secara _default_.\n\n- Kode kamu harus ada di file `extended-clock.js`\n- Jangan ubah `clock.js`. Perpanjang itu!.\n"
  },
  {
    "path": "1-js/09-classes/02-class-inheritance/article.md",
    "content": "# Turunan Kelas\n\nTurunan Kelas adalah cara satu kelas untuk memperluas kelas lainnya.\n\nJadi kita bisa membuat fungsionalitas baru di atas yang sudah ada.\n\n## Kata kunci \"extends\"\n\nKatakanlah kita mempunyai kelas `Animal`:\n\n```js\nclass Animal {\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n  run(speed) {\n    this.speed = speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n  stop() {\n    this.speed = 0;\n    alert(`${this.name} stands still.`);\n  }\n}\n\nlet animal = new Animal('My animal');\n```\n\nInilah cara kita mewakili objek `animal` dan kelas `Animal` secara grafis:\n\n![](rabbit-animal-independent-animal.svg)\n\n...Dan kita akan membuat yang lain `class Rabbit`.\n\nKarena kelinci adalah binatang, kelas `Rabbit` harus didasarkan pada `Animal`, memiliki akses ke metode _animal_, sehingga _rabbits_ dapat melakukan apa yang dapat dilakukan hewan \"pada umumnya\".\n\nSintaks untuk memperluas kelas lain adalah: `class Child extends Parent`.\n\nMari buat `class Rabbit` yang diwarisi dari `Animal`:\n\n```js\n*!*\nclass Rabbit extends Animal {\n*/!*\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\nrabbit.run(5); // White Rabbit runs with speed 5.\nrabbit.hide(); // White Rabbit hides!\n```\n\nObjek dari kelas `Rabbit` mempunyai akses kedua metode `Rabbit`, seperti `rabbit.hide()`, dan juga untuk metode `Animal`, seperti `rabbit.run()`.\n\nSecara internal, kata kunci `extends` bekerja menggunakan mekanik prototipe lama yang bagus. Ini mengatur `Rabbit.prototype.[[Prototype]]` untuk `Animal.prototype`. Jadi, jika metode tidak ditemukan di `Rabbit.prototype`, JavaScript mengambilnya dari `Animal.prototype`.\n\n![](animal-rabbit-extends.svg)\n\nMisalnya, untuk menemukan metode `rabbit.run`, mesin mengecek (dari bawah ke atas pada gambar):\n\n1. Objek `rabbit` (tidak punya `run`).\n2. Prototipenya, yaitu `Rabbit.prototype` (mempunyai `hide`, tapi tidak `run`).\n3. Prototipenya, yaitu (disebabkan oleh `extends`) `Animal.prototype`, yang akhirnya mempunyai metode `run`.\n\nSeperti yang bisa kita ingat dari bab <info:native-prototypes>, JavaScript sendiri menggunakan pewarisan _prototypal_ untuk objek bawaan. Misalnya. `Date.prototype.[[Prototype]]` adalah `Object.prototype`. Itulah mengapa tanggal memiliki akses ke metode objek umum.\n\n````smart header=\"Ekspresi apa pun diperbolehkan setelah `extends` \"Sintaks kelas memungkinkan untuk menentukan tidak hanya kelas, tetapi ekspresi apa pun setelah `extends`.\n\nMisalnya, panggilan fungsi yang menghasilkan kelas induk:\n\n```js run\nfunction f(phrase) {\n  return class {\n    sayHi() { alert(phrase); }\n  };\n}\n\n*!*\nclass User extends f(\"Hello\") {}\n*/!*\n\nnew User().sayHi(); // Hello\n```\n\nDi sini `class User` mewarisi dari hasil `f(\"Hello\")`.\n\nItu mungkin berguna untuk pola pemrograman tingkat lanjut saat kita menggunakan fungsi untuk menghasilkan kelas bergantung pada banyak kondisi dan dapat mewarisinya.\n\n`````\n\n## Mengganti metode\n\nSekarang mari bergerak maju dan mengganti metode. Secara default, semua metode yang tidak dispesifikasikan dalam `class Rabbit` diambil secara langsung \"sebagaimana adanya\" dari `class Animal`.\n\nTetapi jika kita menentukan metode kita sendiri di `Rabbit`, seperti `stop()` maka itu akan digunakan sebagai gantinya:\n\n```js\nclass Rabbit extends Animal {\n  stop() {\n    // ...sekarang ini akan digunakan untuk rabbit.stop()\n    // Bukan stop() dari kelas Animal\n  }\n}\n```\n\nBiasanya kita tidak ingin sepenuhnya mengganti metode induk, melainkan untuk membangun di atasnya untuk mengubah atau memperluas fungsinya. Kita melakukan sesuatu dalam metode kita, tetapi memanggil metode induk sebelum/sesudahnya atau dalam proses.\n\nKelas menyediakan kata kunci `\"super\"` untuk itu.\n\n- `super.method(...)` untuk memanggil metode induk.\n- `super(...)` untuk memanggil konstruktor induk (hanya di dalam konstruktor kita).\n\nMisalnya, biarkan kelinci kita bersembunyi otomatis saat dihentikan:\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n\n  run(speed) {\n    this.speed = speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n\n  stop() {\n    this.speed = 0;\n    alert(`${this.name} stands still.`);\n  }\n\n}\n\nclass Rabbit extends Animal {\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n\n*!*\n  stop() {\n    super.stop(); // memanggil stop induk\n    this.hide(); // dan lalu bersembunyi\n  }\n*/!*\n}\n\nlet rabbit = new Rabbit(\"White Rabbit\");\n\nrabbit.run(5); // White Rabbit runs with speed 5.\nrabbit.stop(); // White Rabbit stands still. White Rabbit hides!\n```\n\nSekarang `Rabbit` mempunyai metode `stop` yang memanggil induk `super.stop()` di dalam proses.\n\n````smart header=\"_Arrow functions_ tidak mempunyai `super`\"\nSeperti yang disebutkan di bab <info:arrow-functions>, _arrow functions_ tidak memiliki `super`.\n\nJika diakses, itu diambil dari fungsi luar. Misalnya:\n```js\nclass Rabbit extends Animal {\n  stop() {\n    setTimeout(() => super.stop(), 1000); // memanggil stop induk setelah 1 detik\n  }\n}\n```\n\n`super` di _arrow function_ sama dengan di `stop()`, jadi berfungsi seperti yang diinginkan. Jika kita menetapkan fungsi \"biasa\" di sini, akan ada kesalahan:\n\n```js\n// Unexpected super\nsetTimeout(function() { super.stop() }, 1000);\n```\n`````\n\n## Mengganti konstruktor\n\nDengan konstruktor, ini menjadi sedikit rumit.\n\nSampai sekarang, `Rabbit` tidak mempunyai `constructor` sendiri.\n\nMenurut [spesifikasi](https://tc39.github.io/ecma262/#sec-runtime-semantics-classdefinitionevaluation), jika sebuah kelas memperluas kelas lain dan tidak memiliki `constructor`, maka `constructor` \"kosong\" berikut akan dibuat:\n\n```js\nclass Rabbit extends Animal {\n  // dihasilkan untuk memperluas kelas tanpa konstruktor sendiri\n*!*\n  constructor(...args) {\n    super(...args);\n  }\n*/!*\n}\n```\n\nSeperti yang bisa kita lihat, ini pada dasarnya memanggil `constructor` induk dengan meneruskan semua argumen. Itu terjadi jika kita tidak menulis konstruktor kita sendiri.\n\nSekarang mari tambahkan konstruktor kustom ke `Rabbit`. Ini akan menentukan `earLength` selain `name`:\n\n```js run\nclass Animal {\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n  // ...\n}\n\nclass Rabbit extends Animal {\n\n*!*\n  constructor(name, earLength) {\n    this.speed = 0;\n    this.name = name;\n    this.earLength = earLength;\n  }\n*/!*\n\n  // ...\n}\n\n*!*\n// Tidak bekerja!\nlet rabbit = new Rabbit(\"White Rabbit\", 10); // Error: this is not defined.\n*/!*\n```\n\nUps! Kita mendapat kesalahan. Sekarang kita tidak bisa membuat _rabbits_. Apa yang salah?\n\nJawaban singkatnya adalah:\n\n- **Konstruktor dalam kelas mewarisi harus memanggil `super(...)`, dan (!) lakukan sebelum menggunakan `this`.**\n\n...Tapi kenapa? Apa yang terjadi di sini? Memang, persyaratannya tampak aneh.\n\nTentu saja ada penjelasannya. Mari kita bahas detailnya, jadi kamu akan benar-benar mengerti apa yang terjadi.\n\nDalam JavaScript, ada perbedaan antara fungsi konstruktor dari kelas yang mewarisi (disebut \"konstruktor turunan\") dan fungsi lainnya. Konstruktor turunan memiliki properti internal khusus `[[ConstructorKind]]:\"turunan\"`. Itu label internal khusus.\n\nLabel itu mempengaruhi perilakunya dengan `new`.\n\n- Saat fungsi reguler dijalankan dengan `new`, itu membuat objek kosong dan menugaskannya ke `this`.\n- Tetapi ketika konstruktor turunan berjalan, ia tidak melakukan ini. Ia mengharapkan konstruktor induk untuk melakukan pekerjaan ini.\n\nJadi, konstruktor turunan harus memanggil `super` untuk menjalankan konstruktor induknya, jika tidak, objek untuk `this` tidak akan dibuat. Dan kita akan mendapatkan kesalahan.\n\nAgar konstruktor `Rabbit` bekerja, ia perlu memanggil`super()`sebelum menggunakan `this`, seperti di sini:\n\n```js run\nclass Animal {\n\n  constructor(name) {\n    this.speed = 0;\n    this.name = name;\n  }\n\n  // ...\n}\n\nclass Rabbit extends Animal {\n\n  constructor(name, earLength) {\n*!*\n    super(name);\n*/!*\n    this.earLength = earLength;\n  }\n\n  // ...\n}\n\n*!*\n// sekarang baik-baik saja\nlet rabbit = new Rabbit(\"White Rabbit\", 10);\nalert(rabbit.name); // White Rabbit\nalert(rabbit.earLength); // 10\n*/!*\n```\n\n### Mengganti bidang kelas: catatan rumit\n\n```warn header=\"Advanced note\"\nCatatan ini mengasumsikan kamu memiliki pengalaman tertentu dengan kelas, mungkin dalam bahasa pemrograman lain.\n\nIni memberikan wawasan yang lebih baik tentang bahasa dan juga menjelaskan perilaku yang mungkin menjadi sumber kesalahan (tetapi tidak terlalu sering).\n\nJika kamu merasa kesulitan untuk memahaminya, lanjutkan saja, lanjutkan membaca, kemudian kembali lagi nanti.\n```\n\nKita tidak hanya dapat mengganti metode, tetapi juga bidang kelas.\n\nMeskipun, ada perilaku rumit saat kami mengakses kolom yang diganti di konstruktor induk, sangat berbeda dari kebanyakan bahasa pemrograman lainnya.\n\nPertimbangkan contoh ini:\n\n```js run\nclass Animal {\n  name = 'animal';\n\n  constructor() {\n    alert(this.name); // (*)\n  }\n}\n\nclass Rabbit extends Animal {\n  name = 'rabbit';\n}\n\nnew Animal(); // animal\n*!*\nnew Rabbit(); // animal\n*/!*\n```\n\nDi sini, kelas `Rabbit` memperluas `Animal` dan mengganti bidang `nama` dengan nilainya sendiri.\n\nTidak ada konstruktor sendiri dalam `Rabbit`, jadi konstruktor `Animal` dipanggil.\n\nYang menarik adalah dalam kedua kasus: `new Animal()` dan `new Rabbit()`, `alert` di baris `(*)` menampilkan `animal`.\n\n**Dengan kata lain, konstruktor induk selalu menggunakan nilai bidangnya sendiri, bukan yang diganti.**\n\nApa yang aneh tentang itu?\n\nJika masih belum jelas silahkan bandingkan dengan metodenya.\n\nBerikut kode yang sama, tetapi alih-alih bidang `this.name` kita memanggil metode `this.showName()`:\n\n```js run\nclass Animal {\n  showName() {  // sebagai ganti this.name = 'animal'\n    alert('animal');\n  }\n\n  constructor() {\n    this.showName(); // sebagai ganti alert(this.name);\n  }\n}\n\nclass Rabbit extends Animal {\n  showName() {\n    alert('rabbit');\n  }\n}\n\nnew Animal(); // animal\n*!*\nnew Rabbit(); // rabbit\n*/!*\n```\n\nHarap diperhatikan: sekarang hasilnya berbeda.\n\nDan itulah yang secara natural kita harapkan. Ketika konstruktor induk dipanggil di kelas turunan, ia menggunakan metode yang diganti.\n\n...Tetapi untuk bidang kelas tidak demikian. Seperti yang dikatakan, konstruktor induk selalu menggunakan bidang induk.\n\nMengapa ada bedanya?\n\nNah, alasannya ada di urutan bidang inisialisasi. Bidang kelas diinisialisasi:\n\n- Sebelum konstruktor untuk kelas dasar (yang tidak memperluas apa pun),\n- Langsung setelah `super()` untuk kelas turunan.\n\nDalam kasus kita, `Rabbit` adalah kelas turunannya. Tidak ada `constructor()` di dalamnya. Seperti yang dikatakan sebelumnya, itu sama seperti jika ada konstruktor kosong hanya dengan `super(...args)`.\n\nJadi, `new Rabbit()` memanggil `super()`, sehingga mengeksekusi konstruktor induk, dan (sesuai aturan untuk kelas turunan) hanya setelah bidang kelasnya diinisialisasi. Pada saat eksekusi induk konstruktor, belum ada bidang kelas `Rabbit`, itulah mengapa bidang `Animal` digunakan.\n\nPerbedaan halus antara bidang dan metode ini khusus untuk JavaScript\n\nUntungnya, perilaku ini hanya muncul dengan sendirinya jika bidang yang diganti digunakan di konstruktor induk. Maka mungkin sulit untuk memahami apa yang sedang terjadi, jadi kita menjelaskannya di sini.\n\nJika ini menjadi masalah, seseorang dapat memperbaikinya dengan menggunakan metode atau _getter_/_setter_ sebagai ganti bidang.\n\n## _Super: internals, [[HomeObject]]_\n\n```warn header=\"Advanced information\"\nJika Anda membaca tutorial untuk pertama kali - bagian ini mungkin dilewati.\n\nIni tentang mekanisme internal di balik pewarisan dan `super`.\n```\n\nMari kita selami lebih dalam di balik tudung `super`. Kita akan melihat beberapa hal menarik di sepanjang jalan.\n\nPertama untuk mengatakan, dari semua yang telah kita pelajari sampai sekarang, mustahil bagi `super` untuk bekerja sama sekali!\n\nYa, memang, mari kita tanyakan pada diri kita sendiri, bagaimana seharusnya secara teknis bekerja? Ketika sebuah metode objek dijalankan, ia mendapatkan objek saat ini sebagai `this`. Jika kita memanggil `super.method()` maka, mesin perlu mendapatkan `metode` dari prototipe objek saat ini. Tapi bagaimana caranya?\n\nTugasnya mungkin tampak sederhana, tetapi sebenarnya tidak. Mesin mengetahui objek saat ini `this`, sehingga bisa mendapatkan `metode` induk sebagai `this.__proto__.method`. Sayangnya, solusi \"naif\" seperti itu tidak akan berhasil.\n\nMari kita tunjukkan masalahnya. Tanpa kelas, menggunakan objek biasa demi kesederhanaan.\n\nKamu dapat melewati bagian ini dan melanjutkan ke subbagian `[[HomeObject]]` jika kamu tidak ingin mengetahui detailnya. Itu tidak akan merugikan. Atau baca terus jika kamu tertarik untuk memahami berbagai hal secara mendalam.\n\nPada contoh di bawah, `rabbit.__ proto__ = animal`. Sekarang mari kita coba: di `rabbit.eat()` kita akan memanggil `animal.eat() `, menggunakan` this.__ proto__`:\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  name: \"Rabbit\",\n  eat() {\n*!*\n    // begitulah kemungkinan super.eat() bisa bekerja\n    this.__proto__.eat.call(this); // (*)\n*/!*\n  }\n};\n\nrabbit.eat(); // Rabbit eats.\n```\n\nPada baris `(*)` kita mengambil `eat` dari prototipe (`animal`) dan memanggilnya dalam konteks objek saat ini. Harap dicatat bahwa `.call(this)` penting di sini, karena sesimpel `this.__proto__.Eat()` akan mengeksekusi `eat` induk dalam konteks prototipe, bukan objek saat ini.\n\nDan pada kode di atas itu benar-benar berfungsi sebagaimana mestinya: kita memiliki `alert` yang benar.\n\nSekarang mari tambahkan satu objek lagi ke rantai. Kita akan melihat bagaimana hal-hal rusak:\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  eat() {\n    // ...melambung di sekitar rabbit-style dan panggil metode induk (animal)\n    this.__proto__.eat.call(this); // (*)\n  }\n};\n\nlet longEar = {\n  __proto__: rabbit,\n  eat() {\n    // ...lakukan sesuatu dengan long ears dan panggil metode induk (rabbit)\n    this.__proto__.eat.call(this); // (**)\n  }\n};\n\n*!*\nlongEar.eat(); // Error: Maximum call stack size exceeded\n*/!*\n```\n\nKode tidak berfungsi lagi! Kita dapat melihat kesalahan saat mencoba memanggil `longEar.eat()`.\n\nMungkin tidak begitu jelas, tetapi jika kita melacak panggilan `longEar.eat()`, maka kita bisa melihat alasannya. Di kedua baris `(*)` dan `(**)` nilai `this` adalah objek saat ini (`longEar`). Itu penting: semua metode objek mendapatkan objek saat ini sebagai `this`, bukan prototipe atau semacamnya.\n\nJadi, di kedua baris `(*)` dan `(**)` nilai dari `this.__ proto__` sama persis: `rabbit`. Mereka keduanya memanggil `rabbit.eat` tanpa naik rantai dalam perulangan tak berujung.\n\nBerikut gambaran tentang apa yang terjadi:\n\n![](this-super-loop.svg)\n\n1. Di dalam `longEar.eat()`, baris `(**)` memanggil `rabbit.eat` dengan menyediakan `this=longEar`.\n   ```js\n   // di dalam longEar.eat() kita punya this = longEar\n   this.__proto__.eat.call(this); // (**)\n   // menjadi\n   longEar.__proto__.eat.call(this);\n   // itu adalah\n   rabbit.eat.call(this);\n   ```\n2. Lalu di baris `(*)` dari `rabbit.eat`, kita ingin meneruskan panggilan lebih tinggi lagi dalam rantai, tapi `this=longEar`, jadi `this.__proto__.eat` lagi-lagi `rabbit.eat`!\n\n   ```js\n   // di dalam rabbit.eat() kita juga punya this = longEar\n   this.__proto__.eat.call(this); // (*)\n   // menjadi\n   longEar.__proto__.eat.call(this);\n   // atau (lagi)\n   rabbit.eat.call(this);\n   ```\n\n3. ...Jadi `rabbit.eat` menyebut dirinya dalam perulangan tak berujung, karena tidak bisa naik lebih jauh.\n\nMasalahnya tidak dapat diselesaikan hanya dengan menggunakan `this`.\n\n### _`[[HomeObject]]`_\n\nUntuk memberikan solusi, JavaScript menambahkan satu lagi properti internal khusus untuk fungsi: `[[HomeObject]]`.\n\nKetika sebuah fungsi ditetapkan sebagai metode kelas atau objek, properti `[[HomeObject]]` -nya menjadi objek itu.\n\nLalu `super` menggunakannya untuk menyelesaikan prototipe induk dan metodenya.\n\nMari kita lihat cara kerjanya, pertama dengan objek biasa:\n\n```js run\nlet animal = {\n  name: \"Animal\",\n  eat() {         // animal.eat.[[HomeObject]] == animal\n    alert(`${this.name} eats.`);\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  name: \"Rabbit\",\n  eat() {         // rabbit.eat.[[HomeObject]] == rabbit\n    super.eat();\n  }\n};\n\nlet longEar = {\n  __proto__: rabbit,\n  name: \"Long Ear\",\n  eat() {         // longEar.eat.[[HomeObject]] == longEar\n    super.eat();\n  }\n};\n\n*!*\n// bekerja dengan benar\nlongEar.eat();  // Long Ear eats.\n*/!*\n```\n\nIni berfungsi sebagaimana mestinya, karena mekanisme `[[HomeObject]]`. Metode, seperti `longEar.eat`, tahu itu `[[HomeObject]]` dan mengambil metode induk dari prototipe-nya. Tanpa menggunakan`this`.\n\n### Metode tidak \"gratis\"\n\nSeperti yang kita ketahui sebelumnya, umumnya fungsi adalah \"gratis\", tidak terikat ke objek di JavaScript. Jadi mereka dapat disalin di antara objek dan dipanggil dengan `this` lainnya.\n\nKeberadaan `[[HomeObject]]` melanggar prinsip itu, karena metode mengingat objeknya. `[[HomeObject]]` tidak bisa diubah, jadi ikatan ini selamanya.\n\nSatu-satunya tempat dalam bahasa di mana `[[HomeObject]]` digunakan -- adalah `super`. Jadi, jika suatu metode tidak menggunakan `super`, maka kita masih bisa menganggapnya gratis dan menyalin antar objek. Tetapi dengan `super` mungkin ada yang salah.\n\nBerikut demo hasil `super` yang salah setelah menyalin:\n\n```js run\nlet animal = {\n  sayHi() {\n    alert(`I'm an animal`);\n  }\n};\n\n// rabbit mewarisi dari animal\nlet rabbit = {\n  __proto__: animal,\n  sayHi() {\n    super.sayHi();\n  }\n};\n\nlet plant = {\n  sayHi() {\n    alert(\"I'm a plant\");\n  }\n};\n\n// tree mewarisi dari plant\nlet tree = {\n  __proto__: plant,\n*!*\n  sayHi: rabbit.sayHi // (*)\n*/!*\n};\n\n*!*\ntree.sayHi();  // I'm an animal (?!?)\n*/!*\n```\n\nPanggilan ke `tree.sayHi()` menunjukkan \"I'm an animal\". Benar-benar salah.\n\nAlasannya sederhana:\n\n- Di baris `(*)`, metode `tree.sayHi` telah disalin dari`rabbit`. Mungkin kita hanya ingin menghindari duplikasi kode?\n- `[[HomeObject]]` -nya adalah `rabbit`, seperti yang dibuat di`rabbit`. Tidak ada cara untuk mengubah `[[HomeObject]]`.\n- Kode `tree.sayHi()` memiliki `super.sayHi()` di dalamnya. Ini naik dari `rabbit` dan mengambil metode dari`animal`.\n\nBerikut diagram dari apa yang terjadi:\n\n![](super-homeobject-wrong.svg)\n\n### Metode, bukan properti fungsi\n\n`[[HomeObject]]` didefinisikan untuk metode baik di kelas maupun di objek biasa. Tetapi untuk objek, metode harus ditentukan persis sebagai `method()`, bukan sebagai `\"method: function()\"`.\n\nPerbedaannya mungkin tidak terlalu penting bagi kita, tetapi penting untuk JavaScript.\n\nDalam contoh di bawah ini, sintaks non-metode digunakan untuk perbandingan. Properti `[[HomeObject]]` tidak diatur dan warisan tidak berfungsi:\n\n```js run\nlet animal = {\n  eat: function() { // sengaja menulis seperti ini, bukan eat() {...\n    // ...\n  }\n};\n\nlet rabbit = {\n  __proto__: animal,\n  eat: function() {\n    super.eat();\n  }\n};\n\n*!*\nrabbit.eat();  // Kesalahan memanggil super (karena tidak ada [[HomeObject]])\n*/!*\n```\n\n## Ringkasan\n\n1. Untuk memperluas kelas: `class Child extends Parent`:\n   - Itu berarti `Child.prototype.__proto__` akan menjadi `Parent.prototype`, jadi metode diwariskan/diturunkan.\n2. Saat mengganti konstruktor:\n   - Kita harus memanggil konstruktor induk sebagai `super()` di konstruktor `Child` sebelum menggunakan `this`.\n3. Saat mengganti metode lain:\n   - Kita dapat menggunakan `super.method()` di metode `Child` untuk memanggil metode `Parent`.\n4. Internal:\n   - Metode mengingat kelas/objek mereka di internal properti `[[HomeObject]]`. Begitulah caranya `super` menyelesaikan metode induk.\n   - Jadi tidak aman untuk menyalin metode dengan `super` dari satu objek ke objek lainnya.\n\nJuga:\n\n- _Arrow functions_ tidak memiliki `this` atau `super` sendiri, sehingga secara transparan sesuai dengan konteks sekitarnya.\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/3-class-extend-object/solution.md",
    "content": "Pertama, mari kita lihat mengapa kode terakhir tidak berfungsi.\n\nAlasannya menjadi jelas jika kita mencoba menjalankannya. Konstruktor kelas yang mewarisi harus memanggil `super()`. Jika tidak, `\"this\"` tidak akan \"defined\".\n\nJadi, inilah perbaikannya:\n\n```js run\nclass Rabbit extends Object {\n  constructor(name) {\n*!*\n    super(); // perlu memanggil konstruktor induk saat mewarisi\n*/!*\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit(\"Rab\");\n\nalert( rabbit.hasOwnProperty('name') ); // true\n```\n\nTapi itu belum semuanya.\n\nBahkan setelah perbaikan, masih ada perbedaan penting dalam `\"class Rabbit extends Object\"` versus `class Rabbit`.\n\nSeperti yang kita tahu, sintaks \"extends\" menyiapkan dua prototipe:\n\n1. Antara `\"prototype\"` dari fungsi konstruktor (untuk metode).\n2. Antara konstruktor berfungsi sendiri (untuk metode statis).\n\nDalam kasus kita, untuk `class Rabbit extends Object` itu berarti:\n\n```js run\nclass Rabbit extends Object {}\n\nalert(Rabbit.prototype.__proto__ === Object.prototype); // (1) true\nalert(Rabbit.__proto__ === Object); // (2) true\n```\n\nJadi `Rabbit` sekarang menyediakan akses ke metode statis `Object` melalui `Rabbit`, seperti ini:\n\n```js run\nclass Rabbit extends Object {}\n\n*!*\n// biasanya kita sebut Object.getOwnPropertyNames\nalert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b\n*/!*\n```\n\nTetapi jika kita tidak punya `extends Object`, lalu `Rabbit.__proto__` tidak diatur ke `Object`.\n\nBerikut demo nya:\n\n```js run\nclass Rabbit {}\n\nalert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true\nalert( Rabbit.__proto__ === Object ); // (2) false (!)\nalert( Rabbit.__proto__ === Function.prototype ); // sebagai fungsi apa pun secara default\n\n*!*\n// error, tidak ada fungsi seperti itu di Rabbit\nalert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error\n*/!*\n```\n\nJadi `Rabbit` tidak menyediakan akses ke metode statis `Object` dalam hal itu.\n\nNgomong-ngomong, `Function.prototype` mempunyai fungsi metode \"generic\", seperti `call`, `bind` dll. Mereka terakhir tersedia dalam kedua kasus, karena untuk konstruktor `Object` bawaan, `Object.__proto__ === Function.prototype`.\n\nBerikut gambarnya:\n\n![](rabbit-extends-object.svg)\n\nJadi, sederhananya, ada dua perbedaan:\n\n| class Rabbit                              | class Rabbit extends Object            |\n| ----------------------------------------- | -------------------------------------- |\n| --                                        | needs to call `super()` in constructor |\n| `Rabbit.__proto__ === Function.prototype` | `Rabbit.__proto__ === Object`          |\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/3-class-extend-object/task.md",
    "content": "importance: 3\n\n---\n\n# _Class extends Object?_\n\nSeperti yang kita ketahui, semua objek biasanya diwarisi dari `Object.prototype` dan mendapatkan akses ke metode objek \"generic\" seperti `hasOwnProperty` dll.\n\nMisalnya:\n\n```js run\nclass Rabbit {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit(\"Rab\");\n\n*!*\n// metode hasOwnProperty dari Object.prototype\nalert( rabbit.hasOwnProperty('name') ); // true\n*/!*\n```\n\nTapi jika kita mengejanya secara eksplisit seperti `\"class Rabbit extends Object\"`, maka hasilnya akan berbeda dari `\"class Rabbit\"`?\n\nApa perbedaannya?\n\nBerikut contoh kodenya (tidak berhasil -- mengapa? memperbaikinya?):\n\n```js\nclass Rabbit extends Object {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\nlet rabbit = new Rabbit('Rab');\n\nalert(rabbit.hasOwnProperty('name')); // Error\n```\n"
  },
  {
    "path": "1-js/09-classes/03-static-properties-methods/article.md",
    "content": "# Properti dan metode statis\n\nKita juga dapat menetapkan metode ke fungsi kelas itu sendiri, bukan ke `\" prototipe \"`-nya. Metode seperti itu disebut _static_.\n\nDi dalam kelas, mereka ditambahkan oleh kata kunci `static`, seperti ini:\n\n```js run\nclass User {\n*!*\n  static staticMethod() {\n*/!*\n    alert(this === User);\n  }\n}\n\nUser.staticMethod(); // true\n```\n\nItu sebenarnya sama dengan menetapkannya sebagai properti secara langsung:\n\n```js run\nclass User {}\n\nUser.staticMethod = function () {\n  alert(this === User);\n};\n\nUser.staticMethod(); // true\n```\n\nNilai `this` dalam panggilan `User.staticMethod()` adalah konstruktor kelas `User` itu sendiri (aturan \"object before dot\").\n\nBiasanya, metode statis digunakan untuk mengimplementasikan fungsi yang dimiliki kelas, tetapi tidak untuk objek tertentu darinya.\n\nMisalnya, kita punya objek `Article` dan membutuhkan sebuah fungsi untuk membandingkan mereka. Solusi natural adalah menambahkan metode `Article.compare`, seperti ini:\n\n```js run\nclass Article {\n  constructor(title, date) {\n    this.title = title;\n    this.date = date;\n  }\n\n*!*\n  static compare(articleA, articleB) {\n    return articleA.date - articleB.date;\n  }\n*/!*\n}\n\n// penggunaan\nlet articles = [\n  new Article(\"HTML\", new Date(2019, 1, 1)),\n  new Article(\"CSS\", new Date(2019, 0, 1)),\n  new Article(\"JavaScript\", new Date(2019, 11, 1))\n];\n\n*!*\narticles.sort(Article.compare);\n*/!*\n\nalert( articles[0].title ); // CSS\n```\n\nDi sini `Article.compare` berdiri \"di atas\" _articles_, sebagai alat untuk membandingkannya. Ini bukan metode _article_, melainkan seluruh kelas.\n\nContoh lain adalah apa yang disebut metode \"factory\". Bayangkan, kita butuh beberapa cara untuk membuat _article_:\n\n1. Buat dengan parameter yang diberikan (`title`, `date` dsb).\n2. Buat _article_ kosong dengan tanggal hari ini.\n3. ...atau yang lainnya.\n\nCara pertama dapat diterapkan oleh konstruktor. Dan untuk yang kedua kita bisa membuat metode statis kelas.\n\nSeperti `Article.createTodays()` di sini:\n\n```js run\nclass Article {\n  constructor(title, date) {\n    this.title = title;\n    this.date = date;\n  }\n\n*!*\n  static createTodays() {\n    // ingat, this = Article\n    return new this(\"Today's digest\", new Date());\n  }\n*/!*\n}\n\nlet article = Article.createTodays();\n\nalert( article.title ); // Today's digest\n```\n\nSekarang setiap kali kita perlu membuat _today's digest_, kita dapat memanggil `Article.createTodays()`. Sekali lagi, itu bukan metode _article_, tapi metode seluruh kelas.\n\nMetode statis juga digunakan dalam kelas terkait basis data untuk mencari/menyimpan/menghapus entri dari basis data, seperti ini:\n\n```js\n// dengan asumsi Article adalah kelas khusus untuk mengelola articles\n// metode statis untuk menghapus article:\nArticle.remove({ id: 12345 });\n```\n\n## Properti Statis\n\n[recent browser=Chrome]\n\nProperti statis juga dimungkinkan, mereka terlihat seperti properti kelas biasa, tetapi diawali dengan `static`:\n\n```js run\nclass Article {\n  static publisher = 'Ilya Kantor';\n}\n\nalert(Article.publisher); // Ilya Kantor\n```\n\nItu sama dengan penugasan langsung ke `Article`:\n\n```js\nArticle.publisher = 'Ilya Kantor';\n```\n\n## Pewarisan properti dan metode statis [#statics-and-inheritance]\n\nProperti dan metode statis diwarisi.\n\nMisalnya, `Animal.compare` dan `Animal.planet` dalam kode di bawah ini diwariskan dan dapat diakses sebagai `Rabbit.compare` dan `Rabbit.planet`:\n\n```js run\nclass Animal {\n  static planet = \"Earth\";\n\n  constructor(name, speed) {\n    this.speed = speed;\n    this.name = name;\n  }\n\n  run(speed = 0) {\n    this.speed += speed;\n    alert(`${this.name} runs with speed ${this.speed}.`);\n  }\n\n*!*\n  static compare(animalA, animalB) {\n    return animalA.speed - animalB.speed;\n  }\n*/!*\n\n}\n\n// Mewarisi dari Animal\nclass Rabbit extends Animal {\n  hide() {\n    alert(`${this.name} hides!`);\n  }\n}\n\nlet rabbits = [\n  new Rabbit(\"White Rabbit\", 10),\n  new Rabbit(\"Black Rabbit\", 5)\n];\n\n*!*\nrabbits.sort(Rabbit.compare);\n*/!*\n\nrabbits[0].run(); // Black Rabbit runs with speed 5.\n\nalert(Rabbit.planet); // Earth\n```\n\nSekarang kita dapat memanggil `Rabbit.compare`, yang diwariskan `Animal.compare` akan dipanggil.\n\nBagaimana cara kerjanya? Sekali lagi, menggunakan prototipe. Seperti yang mungkin sudah kamu duga, `extends` memberi `Rabbit` sebagai `[[Prototype]]` mengacu kepada `Animal`.\n\n![](animal-rabbit-static.svg)\n\nJadi, `Rabbit extends Animal` membuat dua acuan `[[Prototype]]`:\n\n1. `Rabbit` fungsi _prototypally_ mewarisi dari fungsi `Animal`.\n2. `Rabbit.prototype` _prototypally_ mewarisi dari `Animal.prototype`.\n\nHasilnya, pewarisan berfungsi baik untuk metode reguler dan statis.\n\nDi sini, mari kita periksa dengan kode:\n\n```js run\nclass Animal {}\nclass Rabbit extends Animal {}\n\n// untuk statis\nalert(Rabbit.__proto__ === Animal); // true\n\n// untuk metode reguler\nalert(Rabbit.prototype.__proto__ === Animal.prototype); // true\n```\n\n## Ringkasan\n\nMetode statis digunakan untuk fungsionalitas yang termasuk dalam kelas \"secara keseluruhan\". Ini tidak terkait dengan instance kelas konkret.\n\nSebagai contoh, metode perbandingan `Article.compare(article1, article2)` atau metode _factory_ `Article.createTodays()`.\n\nMereka diberi label dengan kata `static` dalam deklarasi kelas.\n\nProperti statis digunakan ketika kita ingin menyimpan data tingkat kelas, juga tidak terikat pada sebuah _instance_.\n\nSintaksnya adalah:\n\n```js\nclass MyClass {\n  static property = ...;\n\n  static method() {\n    ...\n  }\n}\n```\n\nSecara teknis, deklarasi statis sama dengan menetapkan ke kelas itu sendiri:\n\n```js\nMyClass.property = ...\nMyClass.method = ...\n```\n\nProperti dan metode statis diwarisi.\n\nUntuk `class B extends A` prototipe dari kelas `B` itu sendiri menunjuk ke `A`: `B.[[Prototype]] = A`. Jadi jika bidang tidak ditemukan di `B`, pencarian dilanjutkan di `A`.\n"
  },
  {
    "path": "1-js/09-classes/04-private-protected-properties-methods/article.md",
    "content": "# Properti dan metode _private_ dan _protected_\n\nSalah satu prinsip terpenting dari pemrograman berorientasi objek -- membatasi antarmuka internal dari antarmuka eksternal.\n\nItu adalah praktik yang \"harus\" dilakukan dalam mengembangkan sesuatu yang lebih rumit daripada aplikasi \"hello world\".\n\nUntuk memahami ini, mari kita melepaskan diri dari pengembangan dan mengalihkan pandangan kita ke dunia nyata.\n\nBiasanya, perangkat yang kita gunakan cukup rumit. Tetapi membatasi antarmuka internal dari antarmuka eksternal memungkinkan untuk menggunakannya tanpa masalah.\n\n## Contoh kehidupan nyata\n\nMisalnya, mesin kopi. Simpelnya dari luar: tombol, layar, beberapa lubang ... dan tentunya, hasilnya -- kopi yang enak! :)\n\n![](coffee.jpg)\n\nTetapi di dalamnya... (gambar dari panduan perbaikan)\n\n![](coffee-inside.jpg)\n\nBanyak detail. Tetapi kita bisa menggunakannya tanpa mengetahui apapun.\n\nMesin kopi cukup andal, bukan? Kita dapat menggunakannya selama bertahun-tahun, dan jika hanya terjadi kesalahan -- diperbaiki.\n\nRahasia keandalan dan kesederhanaan mesin kopi -- semua detail diatur dengan baik dan _tersembunyi_ di dalamnya.\n\nJika kita melepas tutup pelindung dari mesin kopi, maka menggunakannya akan jauh lebih rumit (di mana harus menekan?), dan berbahaya (dapat menyetrum).\n\nSeperti yang akan kita lihat, dalam pemrograman objek itu seperti mesin kopi.\n\nTetapi untuk menyembunyikan detail bagian dalam, kita tidak akan menggunakan tutup pelindung, melainkan sintaks khusus bahasa dan konvensi.\n\n## Antarmuka internal dan eksternal\n\nDalam pemrograman berorientasi objek, properti dan metode dibagi menjadi 2 kelompok:\n\n- _Antarmuka internal_ -- metode dan properti, dapat diakses dari metode kelas lainnya, tapi tidak dari luar.\n- _Antarmuka eksternal_ -- metode dan properti, dapat diakses juga dari luar kelas.\n\nJika kita melanjutkan analogi dengan mesin kopi -- apa yang tersembunyi di dalam: tabung, elemen pemanas, dan sebagainya -- adalah antarmuka internalnya.\n\nAntarmuka internal digunakan agar objek berfungsi, detailnya saling menggunakan. Misalnya, tabung dipasang ke elemen pemanas.\n\nTetapi dari luar mesin pembuat kopi ditutup oleh tutup pelindungnya, sehingga tidak ada yang bisa menjangkau itu. Detail disembunyikan dan tidak dapat diakses. Kita dapat menggunakan fitur-fiturnya melalui antarmuka eksternal.\n\nJadi, yang kita butuhkan untuk menggunakan sebuah objek adalah mengetahui antarmuka eksternalnya. Kita mungkin sama sekali tidak menyadari cara kerjanya di dalam, dan itu bagus.\n\nItu adalah perkenalan umum.\n\nDi JavaScript, ada dua jenis bidang objek (properti dan metode):\n\n- _Public_: dapat diakses dari mana saja. Mereka terdiri dari antarmuka eksternal. Sampai sekarang kita hanya menggunakan properti dan metode _public_.\n- _Private_: dapat diakses hanya dari dalam kelas. Ini untuk antarmuka internal.\n\nDi banyak bahasa lain juga ada bidang \"protected\": dapat diakses hanya dari dalam kelas dan mereka yang memperluasnya (seperti _private_, tetapi ditambah akses dari kelas yang diwariskan). Mereka juga berguna untuk antarmuka internal. Mereka dalam arti lebih luas daripada yang _private_, karena kita biasanya ingin mewarisi kelas untuk mendapatkan akses ke sana.\n\nBidang _protected_ tidak diterapkan dalam JavaScript pada tingkat bahasa, tetapi dalam praktiknya mereka(_protected_) sangat nyaman, jadi mereka(_protected_) ditiru.\n\nSekarang kita akan membuat mesin kopi di JavaScript dengan semua jenis properti ini. Mesin kopi memiliki banyak detail, kita tidak akan memodelkannya agar tetap sederhana (meskipun kita bisa).\n\n## Melindungi \"waterAmount\"\n\nMari kita buat kelas mesin kopi sederhana dulu:\n\n```js run\nclass CoffeeMachine {\n  waterAmount = 0; // jumlah air di dalamnya\n\n  constructor(power) {\n    this.power = power;\n    alert(`Created a coffee-machine, power: ${power}`);\n  }\n}\n\n// membuat mesin kopi\nlet coffeeMachine = new CoffeeMachine(100);\n\n// tambahkan air\ncoffeeMachine.waterAmount = 200;\n```\n\nSekarang properti `waterAmount` dan `power` adalah publik. Kita dapat dengan mudah mendapatkan/mengatur dari luar ke nilai apa pun.\n\nMari ganti properti `waterAmount` menjadi _protected_ untuk lebih mengontrolnya. Misalnya, kita tidak ingin siapa pun mengaturnya di bawah nol.\n\n**Properti _protected_ biasanya diawali dengan garis bawah `_`.**\n\nItu tidak diberlakukan pada level bahasa, tetapi ada konvensi terkenal antara pemrogram bahwa properti dan metode seperti itu tidak boleh diakses dari luar.\n\nJadi properti kita akan dipanggil `_waterAmount`:\n\n```js run\nclass CoffeeMachine {\n  _waterAmount = 0;\n\n  set waterAmount(value) {\n    if (value < 0) {\n      value = 0;\n    }\n    this._waterAmount = value;\n  }\n\n  get waterAmount() {\n    return this._waterAmount;\n  }\n\n  constructor(power) {\n    this._power = power;\n  }\n}\n\n// membuat mesin kopi\nlet coffeeMachine = new CoffeeMachine(100);\n\n// tambahkan air\ncoffeeMachine.waterAmount = -10; // Error: Negative water\n```\n\nSekarang aksesnya terkendali, jadi pengaturan air di bawah nol gagal.\n\n## _Read-only_ \"power\"\n\nUntuk properti `power`, mari kita membuatnya menjadi _read-only_. Kadang-kadang terjadi bahwa properti harus diatur hanya pada waktu pembuatan, lalu tidak pernah diubah.\n\nPersis seperti itulah kasus mesin kopi: tenaga tidak pernah berubah.\n\nUntuk melakukannya, kita hanya perlu membuat pengambil(_getter_), bukan pengatur(_setter_):\n\n```js run\nclass CoffeeMachine {\n  // ...\n\n  constructor(power) {\n    this._power = power;\n  }\n\n  get power() {\n    return this._power;\n  }\n}\n\n// membuat mesin kopi\nlet coffeeMachine = new CoffeeMachine(100);\n\nalert(`Power is: ${coffeeMachine.power}W`); // Power is: 100W\n\ncoffeeMachine.power = 25; // Error (no setter)\n```\n\n````smart header=\"Getter/setter functions\"\nDi sini kita menggunakan sintaks pengambil/pengatur.\n\nTetapi seringkali fungsi `get.../set...` lebih disukai, seperti ini:\n\n```js\nclass CoffeeMachine {\n  _waterAmount = 0;\n\n  *!*setWaterAmount(value)*/!* {\n    if (value < 0) value = 0;\n    this._waterAmount = value;\n  }\n\n  *!*getWaterAmount()*/!* {\n    return this._waterAmount;\n  }\n}\n\nnew CoffeeMachine().setWaterAmount(100);\n```\n\nItu terlihat sedikit lebih lama, tetapi fungsinya lebih fleksibel. Mereka dapat menerima banyak argumen (bahkan jika kita tidak membutuhkannya sekarang).\n\nDi sisi lain, sintaks get/set lebih pendek, jadi pada akhirnya tidak ada aturan ketat, terserah kamu yang memutuskan.\n````\n\n```smart header=\"Protected fields are inherited\"\nJika kita mewarisi `class MegaMachine extends CoffeeMachine`, maka tidak ada yang menghalangi kita untuk mengakses` this._waterAmount` atau `this._power` dari metode kelas baru.\n\nJadi bidang yang dilindungi tentu saja dapat diwariskan. Tidak seperti privat yang akan kita lihat di bawah.\n```\n\n## Privat \"#waterLimit\"\n\n[recent browser=none]\n\nAda proposal JavaScript yang sudah selesai, hampir dalam standar, yang memberikan dukungan tingkat bahasa untuk properti dan metode privat.\n\nPrivat harus dimulai dengan `#`. Mereka hanya dapat diakses dari dalam kelas.\n\nMisalnya, ada properti privat `#waterLimit` dan metode privat pemeriksaan air `#checkWater`:\n\n```js run\nclass CoffeeMachine {\n*!*\n  #waterLimit = 200;\n*/!*\n\n*!*\n  #fixWaterAmount(value) {\n    if (value < 0) return 0;\n    if (value > this.#waterLimit) return this.#waterLimit;\n  }\n*/!*\n\n  setWaterAmount(value) {\n    this.#waterLimit = this.#fixWaterAmount(value);\n  }\n\n}\n\nlet coffeeMachine = new CoffeeMachine();\n\n*!*\n// tidak dapat mengakses privat dari luar kelas\ncoffeeMachine.#fixWaterAmount(123); // Error\ncoffeeMachine.#waterLimit = 1000; // Error\n*/!*\n```\n\nPada level bahasa, `#` adalah tanda khusus bahwa bidang tersebut bersifat privat. Kita tidak dapat mengaksesnya dari luar atau dari kelas yang diwariskan.\n\nBidang privat tidak bertentangan dengan bidang publik. Kita bisa memiliki bidang privat `#waterAmount` dan publik `waterAmount` pada saat yang sama.\n\nMisalnya, mari jadikan `waterAmount` sebagai pengakses untuk `#waterAmount`:\n\n```js run\nclass CoffeeMachine {\n  #waterAmount = 0;\n\n  get waterAmount() {\n    return this.#waterAmount;\n  }\n\n  set waterAmount(value) {\n    if (value < 0) value = 0;\n    this.#waterAmount = value;\n  }\n}\n\nlet machine = new CoffeeMachine();\n\nmachine.waterAmount = 100;\nalert(machine.#waterAmount); // Error\n```\n\nTidak seperti _protected_, bidang privat diberlakukan oleh bahasa itu sendiri. Itu hal yang bagus.\n\nTetapi jika kita mewarisi dari `CoffeeMachine`, maka kita tidak akan memiliki akses langsung ke`#waterAmount`. Kita harus mengandalkan pengambil/pengatur `waterAmount`:\n\n```js\nclass MegaCoffeeMachine extends CoffeeMachine {\n  method() {\n*!*\n    alert( this.#waterAmount ); // Error: can only access from CoffeeMachine\n*/!*\n  }\n}\n```\n\nDalam banyak skenario, batasan seperti itu terlalu parah. Jika kita memperluas `CoffeeMachine`, kita mungkin memiliki alasan yang sah untuk mengakses internalnya. Itulah mengapa bidang protected lebih sering digunakan, meskipun tidak didukung oleh sintaks bahasa.\n\n````warn header=\"Private fields are not available as this[name]\"\nBidang privat itu istimewa.\n\nSeperti yang kita ketahui, biasanya kita bisa mengakses bidang menggunakan `this[name]`:\n\n```js\nclass User {\n  ...\n  sayHi() {\n    let fieldName = \"name\";\n    alert(`Hello, ${*!*this[fieldName]*/!*}`);\n  }\n}\n```\n\nDengan bidang privat itu tidak mungkin: `this['#name']` tidak berfungsi. Itu adalah batasan sintaks untuk memastikan privasi.\n````\n\n## Ringkasan\n\nDalam istilah PBO (Pemrograman Berorientasi Objek), membatasi antarmuka internal dari antarmuka eksternal disebut [enkapsulasi](<https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)>).\n\nItu memberi manfaat sebagai berikut:\n\nPerlindungan bagi pengguna, agar mereka tidak menembak diri sendiri\n: Bayangkan, ada tim pengembang yang menggunakan mesin kopi. Itu dibuat oleh perusahaan \"Mesin Kopi Terbaik\", dan berfungsi dengan baik, tetapi penutup pelindungnya dilepas. Jadi antarmuka internal terekspos.\n\n    Semua pengembang sopan -- mereka menggunakan mesin kopi sebagaimana mestinya. Tapi salah satu dari mereka, John, memutuskan bahwa dialah yang paling pintar, dan membuat beberapa perubahan di internal mesin kopi. Jadi mesin kopi mati dua hari kemudian.\n\n    Itu jelas bukan salah John, melainkan orang yang melepas penutup pelindung dan membiarkan John melakukan manipulasinya.\n\n    Hal yang sama dalam pemrograman. Jika pengguna kelas akan mengubah hal-hal yang tidak dimaksudkan untuk diubah dari luar -- konsekuensinya tidak dapat diprediksi.\n\nDidukung\n: Situasi dalam pemrograman lebih rumit daripada dengan mesin kopi di kehidupan nyata, karena kita tidak hanya membelinya sekali. Kode terus mengalami pengembangan dan peningkatan.\n\n    **Jika kita membatasi secara ketat antarmuka internal, maka pengembang kelas dapat dengan bebas mengubah properti dan metode internalnya, bahkan tanpa memberi tahu pengguna.**\n\n    Jika kamu adalah pengembang dari kelas seperti itu, senang mengetahui bahwa metode privat dapat diubah namanya dengan aman, parameternya dapat diubah, dan bahkan dihapus, karena tidak ada kode eksternal yang bergantung padanya.\n\n    Untuk pengguna, ketika versi baru keluar, itu mungkin perombakan total secara internal, tetapi masih mudah untuk meningkatkan jika antarmuka eksternal sama.\n\nMenyembunyikan kerumitan\n: Orang suka menggunakan hal-hal yang sederhana. Setidaknya dari luar. Apa yang ada di dalamnya adalah hal yang berbeda.\n\n    Pemrogram tidak terkecuali.\n\n    **Selalu nyaman jika detail implementasi disembunyikan, dan tersedia antarmuka eksternal yang sederhana dan terdokumentasi dengan baik.**\n\nUntuk menyembunyikan antarmuka internal kita menggunakan properti _protected_ atau privat:\n\n- Bidang _protected_ dimulai dengan `_`. Itu konvensi yang terkenal, tidak diberlakukan di tingkat bahasa. Pemrogram hanya boleh mengakses bidang yang dimulai dengan `_` dari kelasnya dan kelas yang mewarisinya.\n- Bidang privat dimulai dengan `#`. JavaScript memastikan kita hanya dapat mengakses mereka dari dalam kelas.\n\nSaat ini, bidang privat tidak didukung dengan baik di antara browser, tetapi dapat di-polyfill.\n"
  },
  {
    "path": "1-js/09-classes/05-extend-natives/article.md",
    "content": "# Meng-_extend_ `class` bawaan\n\n`class` bawaan seperti Array, Map dan lainnya juga dapat di-_extend_.\n\nContohnya, `PowerArray` disini mewarisi dari _native_ `Array`:\n\n```js run\n// menambahkan satu method ke dalam `PowerArray` (bisa lebih)\nclass PowerArray extends Array {\n  isEmpty() {\n    return this.length === 0;\n  }\n}\n\nlet arr = new PowerArray(1, 2, 5, 10, 50);\nalert(arr.isEmpty()); // false\n\nlet filteredArr = arr.filter((item) => item >= 10);\nalert(filteredArr); // 10, 50\nalert(filteredArr.isEmpty()); // false\n```\n\nMohon perhatikan hal yang sangat menarik. _Method_ bawaan seperti `filter`, `map`, dan yang lainnya -- mengembalikan objek baru dengan tipe `PowerArray` yang diwariskan. Implementasi didalamnya menggunakan properti `constructor` untuk hal itu.\n\nPada contoh di atas,\n\n```js\narr.constructor === PowerArray;\n```\n\nSaat `arr.filter()` dipanggil, secara internal itu membuat senarai hasil yang baru menggunakan `arr.constructor`, bukan _basic_ `Array`. Itu sebenarnya sangat menakjubkan, karena kita bisa tetap menggunakan `PowerArray` _method_ lebih jauh.\n\nBahkan, kita bisa menyesuaikan perilaku khusus untuk itu.\n\nKita bisa menambahkan _static getter_ `Symbol.species` ke dalam `class`. Jika ada, ia harus mengembalikan `constructor` yang akan JavaScript gunakan secara internal untuk membuat entitas baru di dalam `map`, `filter`, dan seterusnya.\n\nJika kita ingin _method_ bawaan seperti `map` atau `filter` untuk mengembalikan senarai biasa, kita bisa mengembalikannya di dalam `Symbol.species`, seperti contohnya disini:\n\n```js run\nclass PowerArray extends Array {\n  isEmpty() {\n    return this.length === 0;\n  }\n\n  // method bawaan menggunakan ini sebagai `constructor`\n  static get [Symbol.species]() {\n    return Array;\n  }\n}\n\nlet arr = new PowerArray(1, 2, 5, 10, 50);\nalert(arr.isEmpty()); // false\n\n// `filter` membuat senarai baru `arr.constructor[Symbol.species]` sebagai `constructor`\nlet filteredArr = arr.filter((item) => item >= 10);\n\n// filteredArr bukan PowerArray, tapi sebuah Array\nalert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function\n```\n\nSeperti yang anda lihat, sekarang `.filter` mengembalikan `Array`. Jadi _function_ yang di-_extend_ tidak diteruskan.\n\n```smart header=\"Other collections work similarly\"\nKoleksi lain, seperti `Map` dan `Set`, berfungsi sama. Mereka juga menggunakan `Symbol.species`.\n```\n\n## Tidak ada _static inheritance_ pada _built-in_\n\nObjek bawaan memiliki _static method_-nya sendiri, misalnya `Object.keys`, `Array.isArray`, dll.\n\nSeperti yang kita tahu, _native class_ meng-_extend_ satu sama lain. Misalnya, `Array` meng-_extend_ `Object`.\n\nBiasanya, ketika sebuah `class` meng-_extend_ `class` yang lainnya, kedua _method static_ dan _non-static_ akan diwariskan. Itu dijelaskan sepenuhnya dalam artikel [](info:static-properties-methods#statics-and-inheritance).\n\nTapi `class` bawaan adalah pengecualian. Mereka tidak mewarisi satu sama lain.\n\nContohnya, `Array` dan `Date` mewarisi dari `Object`, sehingga _instance_ mereka memiliki _method_ dari `Object.prototype`. Tapi `Array.[[Prototype]]` tidak mereferensikan `Object`, sehingga tidak ada, semisalnya, `Array.keys()` (atau `Date.keys()`) _static method_.\n\nBerikut adalah struktur gambar untuk `Date` dan `Object`:\n\n![](object-date-inheritance.svg)\n\nSeperti yang Anda lihat, tidak ada hubungan antara `Date` dan `Object`. Mereka independen, hanya `Date.prototype` mewarisi dari `Object.prototype`.\n\nItu adalah perbedaan penting dari warisan antara objek bawaan dibandingkan dengan apa yang kita dapat dengan `extends`.\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/1-strange-instanceof/solution.md",
    "content": "Yah, memang terlihat aneh.\n\nTetapi `instanceof` tidak peduli dengan fungsinya, melainkan tentang `prototype`, yang cocok dengan rantai prototipe.\n\nDan di sini `a.__proto__ == B.prototype`, jadi `instanceof` mengembalikan `true`.\n\nJadi, dengan menggunakan logika `instanceof`, `prototype` sebenarnya mendefinisikan tipe, bukan fungsi konstruktor.\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/1-strange-instanceof/task.md",
    "content": "importance: 5\n\n---\n\n# \"instanceof\" Aneh\n\nPada kode dibawah, kenapa `instanceof` mengembalikan `true`? Dengan jelas kita dapat melihat `a` tidak dibuat oleh `B()`.\n\n```js run\nfunction A() {}\nfunction B() {}\n\nA.prototype = B.prototype = {};\n\nlet a = new A();\n\n*!*\nalert( a instanceof B ); // true\n*/!*\n```\n"
  },
  {
    "path": "1-js/09-classes/06-instanceof/article.md",
    "content": "# Pengecekan kelas: \"instanceof\"\n\nOperator `instanceof` memungkinkan kita untuk memeriksa apakah suatu _object_ milik _class_ tertentu. `instanceof` juga memperhatikan _inheritance_.\n\nPengecekan seperti itu mungkin diperlukan dalam beberapa kasus. Di sini kita akan menggunakannya untuk membangun fungsi *polymorphic*, yang memperlakukan argumen secara berbeda bergantung pada tipenya.\n\n## Operator instanceof [#ref-instanceof]\n\nSintaksnya adalah:\n```js\nobj instanceof Class\n```\n\nAkan mengembalikan `true` jika `obj` dimiliki oleh `Class` atau kelas turunannya.\n\nMisalnya:\n\n```js run\nclass Rabbit {}\nlet rabbit = new Rabbit();\n\n// apakah ini merupakan object dari kelas Rabbit?\n*!*\nalert( rabbit instanceof Rabbit ); // true\n*/!*\n```\n\nItu juga bekerja dengan fungsi _constructor_:\n\n```js run\n*!*\n// dari pada class\nfunction Rabbit() {}\n*/!*\n\nalert( new Rabbit() instanceof Rabbit ); // true\n```\n\n...Dan kelas bawaan seperti `Array`:\n\n```js run\nlet arr = [1, 2, 3];\nalert( arr instanceof Array ); // true\nalert( arr instanceof Object ); // true\n```\n\nHarap dicatat bahwa `arr` juga termasuk dalam kelas `Object`. Itu karena `Array` secara prototipikal mewarisi dari` Object`.\n\nNormalnya, `instanceof` memeriksa rantai _prototype_ untuk pemeriksaan. Kita juga dapat menyetel _custom logic_ dalam _static method_ `Symbol.hasInstance`.\n\nAlgoritma `obj instanceof Class` bekerja kurang lebih sebgai berikut:\n\n1. Jika terdapat _static method_ `Symbol.hasInstance`, maka panggil saja: `Class[Symbol.hasInstance](obj)`. Itu akan mengembalikan `true` atau `false`, selesai. Begitulah cara kita bisa menyesuaikan perilaku dari `instanceof`.\n\n    Sebagai contoh:\n\n    ```js run\n    // menyiapkan instanceOf yang berasumsi\n    // apapun yang memiliki properti canEat adalah binatang\n    class Animal {\n      static [Symbol.hasInstance](obj) {\n        if (obj.canEat) return true;\n      }\n    }\n\n    let obj = { canEat: true };\n\n    alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) dipanggil\n    ```\n\n2. Kebanyakan kelas tidak memiliki `Symbol.hasInstance`. Dalam kasus ini, logika standar digunakan: `obj instanceOf Class` Memeriksa apakah `Class.prototype` sama dengan salah satu _prototype_ dalam rantai _prototype_`obj` .\n\n    Dengan kata lain, bandingkan satu sama lain:\n    ```js\n    obj.__proto__ === Class.prototype?\n    obj.__proto__.__proto__ === Class.prototype?\n    obj.__proto__.__proto__.__proto__ === Class.prototype?\n    ...\n    // jika jawabannya true, return true\n    // jika tidak, jika kita mencapai ujung rantai, return false\n    ```\n\n    Pada contoh diatas `rabbit.__proto__ === Rabbit.prototype`, sehingga akan memberikan jawaban segera.\n\n    Dalam kasus _inheritance_, kesamaan akan berada pada langkah kedua:\n\n    ```js run\n    class Animal {}\n    class Rabbit extends Animal {}\n\n    let rabbit = new Rabbit();\n    *!*\n    alert(rabbit instanceof Animal); // true\n    */!*\n\n    // rabbit.__proto__ === Rabbit.prototype\n    *!*\n    // rabbit.__proto__.__proto__ === Animal.prototype (match!)\n    */!*\n    ```\n\nBerikut ilustrasi tentang perbandingan `rabbit instanceof Animal` dengan `Animal.prototype`:\n\n![](instanceof.svg)\n\nOmong-omong, terdapat juga _method_ [objA.isPrototypeOf(objB)](mdn:js/object/isPrototypeOf), yang mengembalikan `true` jika `objA` berada di suatu tempat dalam rantai _prototypes_ untuk `objB`. Jadi pengujian `obj instanceof Class` bisa dirumuskan sebagai `Class.prototype.isPrototypeOf(obj)`.\n\nIni lucu, tetapi _constructor_ `Class` itu sendiri tidak ikut dalam pemeriksaan! Hanya rangkaian dari _prototype_ dan `Class.prototype` yang penting.\n\nItu bisa menimbulkan konsekuensi yang menarik ketika properti `prototype` diubah setelah objek dibuat.\n\nSeperti:\n\n```js run\nfunction Rabbit() {}\nlet rabbit = new Rabbit();\n\n// mengubah prototype\nRabbit.prototype = {};\n\n// ...bukan kelinci lagi!\n*!*\nalert( rabbit instanceof Rabbit ); // false\n*/!*\n```\n\n## Bonus: Object.prototype.toString untuk tipe\n\nKita tahu bahwa _plain object_ akan diubah menjadi _string_ sebagai `[object Object]`:\n\n```js run\nlet obj = {};\n\nalert(obj); // [object Object]\nalert(obj.toString()); // sama\n```\n\nItulah implementasi dari `toString`. Tetapi ada fitur tersembunyi yang membuat `toString` menjadi lebih _powerful_ dari itu. Kita bisa menggunakannya sebagai perluasan dari `typeof` dan alternatif untuk `instanceof`.\n\nTerdengar aneh? Tentu. Mari kita cari tahu.\n\nDengan [spesifikasi](https://tc39.github.io/ecma262/#sec-object.prototype.tostring), `toString` bawaan dapat diekstrak dari object dan dijalankan dalam konteks nilai lainnya. Dan hasilnya tergantung pada nilai tersebut.\n\n- Untuk angka, akan menjadi `[object Number]`\n- Untuk _boolean_, akan menjadi `[object Boolean]`\n- Untuk `null`: `[object Null]`\n- Untuk `undefined`: `[object Undefined]`\n- Untuk _array_: `[object Array]`\n- ...dll (dapat disesuaikan).\n\nMari kita tunjukkan:\n\n```js run\n// copy toString method kedalam sebuah variabel\nlet objectToString = Object.prototype.toString;\n\n// tipe apa ini?\nlet arr = [];\n\nalert( objectToString.call(arr) ); // [object *!*Array*/!*]\n```\n\nDisini kita gunakan [_call_](mdn:js/function/call) seperti dijelaskan dalam bab [](info:call-apply-decorators) untuk menjalankan fungsi `objectToString` dalam konteks `this=arr`.\n\nSecara internal, algoritme `toString` memeriksa `this` dan mengembalikan hasil yang sesuai. Contoh lainnya:\n\n```js run\nlet s = Object.prototype.toString;\n\nalert( s.call(123) ); // [object Number]\nalert( s.call(null) ); // [object Null]\nalert( s.call(alert) ); // [object Function]\n```\n\n### Symbol.toStringTag\n\nPerilaku object `toString` dapat disesuaikan dengan menggunakan properti objek khusus `Symbol.toStringTag`.\n\nSebagai contoh:\n\n```js run\nlet user = {\n  [Symbol.toStringTag]: \"User\"\n};\n\nalert( {}.toString.call(user) ); // [object User]\n```\n\nUntuk sebagian besar objek yang spesifik pada _environment_, terdapat properti seperti itu. Berikut beberapa contoh untuk browser yang spesifik:\n\n```js run\n// toStringTag untuk objek dan kelas yang spesifik pada environtment:\nalert( window[Symbol.toStringTag]); // Window\nalert( XMLHttpRequest.prototype[Symbol.toStringTag] ); // XMLHttpRequest\n\nalert( {}.toString.call(window) ); // [object Window]\nalert( {}.toString.call(new XMLHttpRequest()) ); // [object XMLHttpRequest]\n```\n\nDapat dilihat, hasilnya persis `Symbol.toStringTag` (jika ada), digabungkan ke dalam `[object ...]`.\n\nPada akhirnya kami memiliki _\"typeof on steroids\"_ yang tidak hanya akan berfungsi untuk tipe data primitif, tetapi juga untuk objek bawaan dan bahkan dapat disesuaikan.\n\nKita dapat menggunakan `{}.toString.call` daripada `instanceof` untuk objek bawaan ketika ingin mendapatkan tipe sebagai string daripada hanya untuk diperiksa.\n\n## Ringkasan\n\nMari kita rangkum metode pengecekan tipe yang kita ketahui:\n\n|               | bekerja pada   |  mengembalikan      |\n|---------------|-------------|---------------|\n| `typeof`      | primitif  |  string       |\n| `{}.toString` | primitif, objek bawaan, objek dengan `Symbol.toStringTag`   |       string |\n| `instanceof`  | objek     |  true/false   |\n\nDapat kita lihat, `{}.toString` secara teknis \"lebih _advanced_\" `typeof`.\n\nDan operator `instanceof` akan lebih berguna ketika kita bekerja dengan hirearki kelas dan ingin memeriksa kelas yang memperhatikan _inheritance_.\n"
  },
  {
    "path": "1-js/09-classes/07-mixins/article.md",
    "content": "# _Mixins_\n\nDalam JavaScript kita hanya dapat mewarisi dari satu objek. Hanya ada satu `[[Prototype]]` untuk sebuah objek. Dan sebuah kelas hanya dapat memperluas satu kelas lainnya.\n\nTapi terkadang hal itu terasa membatasi. Misalnya, kita memiliki kelas `StreetSweeper` dan kelas `Bicycle`, dan ingin membuat campurannya: `StreetSweepingBicycle`.\n\nAtau kita memiliki kelas `User` dan kelas `EventEmitter` yang mengimplementasikan pembuatan peristiwa, dan kita ingin menambahkan fungsionalitas `EventEmitter` ke `User`, sehingga pengguna kita dapat memancarkan peristiwa.\n\nAda konsep yang bisa membantu di sini, yang disebut \"mixin\".\n\nSeperti yang didefinisikan di Wikipedia, [_mixin_](https://en.wikipedia.org/wiki/Mixin) adalah kelas yang berisi metode yang dapat digunakan oleh kelas lain tanpa perlu mewarisinya.\n\nDengan kata lain, _mixin_ menyediakan metode yang mengimplementasikan perilaku tertentu, tetapi kami tidak menggunakannya sendiri, kita menggunakannya untuk menambahkan perilaku ke kelas lain.\n\n## Contoh _mixin_\n\nCara termudah untuk mengimplementasikan _mixin_ dalam JavaScript adalah membuat objek dengan metode yang berguna, sehingga kita dapat dengan mudah menggabungkannya menjadi prototipe kelas apa pun.\n\nMisalnya di sini mixin `sayHiMixin` digunakan untuk menambahkan beberapa \"ucapan\" untuk `User`:\n\n```js run\n*!*\n// mixin\n*/!*\nlet sayHiMixin = {\n  sayHi() {\n    alert(`Hello ${this.name}`);\n  },\n  sayBye() {\n    alert(`Bye ${this.name}`);\n  }\n};\n\n*!*\n// penggunaan:\n*/!*\nclass User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\n// salin metodenya\nObject.assign(User.prototype, sayHiMixin);\n\n// sekarang User dapat berkata hi\nnew User(\"Dude\").sayHi(); // Hello Dude!\n```\n\nTidak ada warisan, tetapi penyalinan metode sederhana. Jadi, `User` dapat mewarisi dari kelas lain dan juga menyertakan _mixin_ untuk \"mencampur\" metode tambahan, seperti ini:\n\n```js\nclass User extends Person {\n  // ...\n}\n\nObject.assign(User.prototype, sayHiMixin);\n```\n\n_Mixin_ dapat memanfaatkan warisan di dalam dirinya sendiri.\n\nMisalnya, di sini `sayHiMixin` mewarisi dari `sayMixin`:\n\n```js run\nlet sayMixin = {\n  say(phrase) {\n    alert(phrase);\n  }\n};\n\nlet sayHiMixin = {\n  __proto__: sayMixin, // (atau kita bisa gunakan Object.create untuk mengatur prototipe di sini)\n\n  sayHi() {\n    *!*\n    // panggil metode induk\n    */!*\n    super.say(`Hello ${this.name}`); // (*)\n  },\n  sayBye() {\n    super.say(`Bye ${this.name}`); // (*)\n  }\n};\n\nclass User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n\n// salin metodenya\nObject.assign(User.prototype, sayHiMixin);\n\n// sekarang User dapat berkata hi\nnew User(\"Dude\").sayHi(); // Hello Dude!\n```\n\nPerhatikan bahwa panggilan ke metode induk `super.say()` dari `sayHiMixin` (pada baris berlabel `(*)`) mencari metode dalam prototipe _mixin_ tersebut, bukan kelasnya.\n\nBerikut diagramnya (lihat bagian kanan):\n\n![](mixin-inheritance.svg)\n\nItu karena metode `sayHi` dan `sayBye` awalnya dibuat di `sayHiMixin`. Jadi, meskipun disalin, properti internal `[[HomeObject]]` mereferensikan `sayHiMixin`, seperti yang ditunjukkan pada gambar di atas.\n\nKarena `super` mencari metode induk di `[[HomeObject]].[[Prototype]]`, itu berarti mencari `sayHiMixin.[[Prototype]]`, bukan `User.[[Prototype]]`.\n\n## Peristiwa _Mixin_\n\nSekarang mari kita membuat _mixin_ untuk kehidupan nyata.\n\nFitur penting dari banyak objek browser (misalnya) adalah mereka dapat menghasilkan peristiwa. Peristiwa adalah cara terbaik untuk \"menyiarkan informasi\" kepada siapa pun yang menginginkannya. Jadi mari kita membuat _mixin_ yang memungkinkan kita dengan mudah menambahkan fungsi terkait peristiwa ke kelas/objek apa pun.\n\n- _Mixin_ akan menyediakan metode `.trigger(name, [...data])` untuk \"menghasilkan peristiwa\" ketika sesuatu yang penting terjadi padanya. Argumen `name` adalah nama peristiwa, secara opsional diikuti oleh argumen tambahan dengan data peristiwa.\n- Juga metode `.on(name, handler)` yang menambahkan fungsi `handler` sebagai pendengar peristiwa dengan nama yang diberikan. Ini akan dipanggil ketika sebuah peristiwa dengan `name` yang diberikan terpicu, dan mendapatkan argumen dari panggilan `.trigger`.\n- ...Dan metode `.off(name, handler)` yang menghapus pendengar `handler`.\n\nSetelah menambahkan _mixin_, sebuah objek `user` akan dapat membuat peristiwa `\"login\"` saat pengunjung masuk. Dan objek lain, katakanlah, `calendar` mungkin ingin mendengarkan peristiwa seperti itu untuk memuat kalender bagi orang yang masuk.\n\nAtau, `menu` dapat menghasilkan peristiwa `\"select\"` ketika item menu dipilih, dan objek lain dapat menetapkan penangan untuk bereaksi pada peristiwa itu. Dan seterusnya.\n\nBerikut kodenya:\n\n```js run\nlet eventMixin = {\n  /**\n   * Berlangganan ke peristiwa, menggunakan:\n   *  menu.on('select', function(item) { ... }\n   */\n  on(eventName, handler) {\n    if (!this._eventHandlers) this._eventHandlers = {};\n    if (!this._eventHandlers[eventName]) {\n      this._eventHandlers[eventName] = [];\n    }\n    this._eventHandlers[eventName].push(handler);\n  },\n\n  /**\n   * Membatalkan langganan, menggunakan:\n   *  menu.off('select', handler)\n   */\n  off(eventName, handler) {\n    let handlers = this._eventHandlers?.[eventName];\n    if (!handlers) return;\n    for (let i = 0; i < handlers.length; i++) {\n      if (handlers[i] === handler) {\n        handlers.splice(i--, 1);\n      }\n    }\n  },\n\n  /**\n   * menghasilkan peristiwa dengan nama dan data yang diberikan\n   *  this.trigger('select', data1, data2);\n   */\n  trigger(eventName, ...args) {\n    if (!this._eventHandlers?.[eventName]) {\n      return; // tidak ada penangan untuk nama peristiwa itu\n    }\n\n    // panggil penangannya\n    this._eventHandlers[eventName].forEach((handler) =>\n      handler.apply(this, args)\n    );\n  },\n};\n```\n\n- `.on(eventName, handler)` -- menetapkan fungsi `handler` untuk dijalankan ketika peristiwa dengan nama itu terjadi. Secara teknis, ada properti `_eventHandlers` yang menyimpan senarai penangan untuk setiap nama peristiwa, dan itu hanya menambahkannya ke daftar.\n- `.off(eventName, handler)` -- menghapus fungsi dari daftar penangan.\n- `.trigger(eventName, ...args)` -- menghasilkan peristiwa: semua penangan dari `_eventHandlers[eventName]` dipanggil, dengan daftar argumen `...args`.\n\nPenggunaan:\n\n```js run\n// Membuat kelas\nclass Menu {\n  choose(value) {\n    this.trigger(\"select\", value);\n  }\n}\n// Tambahkan mixin dengan metode terkait peristiwa\nObject.assign(Menu.prototype, eventMixin);\n\nlet menu = new Menu();\n\n// tambahkan penangan, untuk dipanggil saat seleksi:\n*!*\nmenu.on(\"select\", value => alert(`Value selected: ${value}`));\n*/!*\n\n// memicu peristiwa => penangan di atas berjalan dan menunjukkan:\n// Value selected: 123\nmenu.choose(\"123\");\n```\n\nSekarang, jika kita ingin kode apa pun bereaksi terhadap pilihan menu, kita dapat mendengarkannya dengan `menu.on(...)`.\n\nDan _mixin_ `eventMixin` membuatnya mudah untuk menambahkan perilaku tersebut ke sebanyak mungkin kelas yang kita inginkan, tanpa mengganggu rantai pewarisan.\n\n## Ringkasan\n\n_Mixin_ -- adalah istilah umum pemrograman berorientasi objek: kelas yang berisi metode untuk kelas lain.\n\nBeberapa bahasa lain mengizinkan banyak pewarisan. JavaScript tidak mendukung banyak pewarisan, tetapi _mixin_ dapat diimplementasikan dengan menyalin metode ke dalam prototipe.\n\nKita bisa menggunakan _mixin_ sebagai cara untuk menambah kelas dengan menambahkan beberapa perilaku, seperti penanganan peristiwa seperti yang telah kita lihat di atas.\n\n_Mixin_ dapat menjadi titik konflik jika secara tidak sengaja menimpa metode kelas yang ada. Jadi umumnya orang harus berpikir dengan baik tentang metode penamaan _mixin_, untuk meminimalkan kemungkinan hal itu terjadi.\n"
  },
  {
    "path": "1-js/09-classes/07-mixins/head.html",
    "content": "<script>\n  let eventMixin = {\n    /**\n     * Berlangganan ke peristiwa, menggunakan:\n     *  menu.on('select', function(item) { ... }\n     */\n    on(eventName, handler) {\n      if (!this._eventHandlers) this._eventHandlers = {};\n      if (!this._eventHandlers[eventName]) {\n        this._eventHandlers[eventName] = [];\n      }\n      this._eventHandlers[eventName].push(handler);\n    },\n\n    /**\n     * Membatalkan langganan, menggunakan:\n     *  menu.off('select', handler)\n     */\n    off(eventName, handler) {\n      let handlers = this._eventHandlers?.[eventName];\n      if (!handlers) return;\n      for (let i = 0; i < handlers.length; i++) {\n        if (handlers[i] == handler) {\n          handlers.splice(i--, 1);\n        }\n      }\n    },\n\n    /**\n     * Menghasilkan peristiwa dan lampirkan data ke dalamnya\n     *  this.trigger('select', data1, data2);\n     */\n    trigger(eventName, ...args) {\n      if (!this._eventHandlers || !this._eventHandlers[eventName]) {\n        return; // tidak ada penangan untuk nama peristiwa itu\n      }\n\n      // panggil penangannya\n      this._eventHandlers[eventName].forEach((handler) =>\n        handler.apply(this, args)\n      );\n    },\n  };\n</script>\n"
  },
  {
    "path": "1-js/09-classes/index.md",
    "content": "# Kelas\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/1-finally-or-code-after/solution.md",
    "content": "Perbedaannya menjadi jelas ketika kita melihat kode di dalam suatu fungsi.\n\nPerilakunya berbeda jika ada lompatan keluar dari `try..catch`.\n\nMisalnya, ketika ada `return` di dalam` try..catch`. Klausa `finally` berfungsi jika *ada* ya keluar dari` try..catch`, bahkan melalui pernyataan `return`: tepat setelah` try..catch` selesai, tetapi sebelum kode pemanggil mendapatkan kontrol.\n\n```js run\nfunction f() {\n  try {\n    alert('start');\n*!*\n    return \"result\";\n*/!*\n  } catch (err) {\n    /// ...\n  } finally {\n    alert('cleanup!');\n  }\n}\n\nf(); // cleanup!\n```\n\n.. Atau ketika ada `throw`, seperti di sini:\n\n```js run\nfunction f() {\n  try {\n    alert('start');\n    throw new Error(\"an error\");\n  } catch (err) {\n    // ...\n    if(\"can't handle the error\") {\n*!*\n      throw err;\n*/!*\n    }\n\n  } finally {\n    alert('cleanup!')\n  }\n}\n\nf(); // cleanup!\n```\n\nBagian `finally` yang menjamin proses pembersihan di sini. Jika kita hanya meletakkan kode di akhir `f`, itu tidak akan berjalan dalam situasi ini.\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/1-finally-or-code-after/task.md",
    "content": "Nilai Penting: 5\n\n---\n\n# Finally atau hanya kode biasa?\n\nBandingkan dua fragmen kode.\n\n1. Yang pertama menggunakan `finally` untuk mengeksekusi kode setelah` try..catch`:\n\n    ```js\n    try {\n      work work\n    } catch (err) {\n      handle errors\n    } finally {\n    *!*\n      cleanup the working space\n    */!*\n    }\n    ```\n2. Fragmen kedua melakukan pembersihan tepat setelah `try..catch`:\n\n    ```js\n    try {\n      work work\n    } catch (err) {\n      handle errors\n    }\n\n    *!*\n    cleanup the working space\n    */!*\n    ```\n\nKita pasti membutuhkan pembersihan setelah pekerjaan, tidak masalah apakah ada kesalahan atau tidak.\n\nApakah ada keuntungan di sini dalam menggunakan `finally` atau kedua fragmen kode sama? Jika ada keuntungan seperti itu, berikan contoh ketika itu penting.\n"
  },
  {
    "path": "1-js/10-error-handling/1-try-catch/article.md",
    "content": "# Penanganan eror, \"try..catch\"\n\n\nTidak peduli seberapa hebatnya kita dalam pemograman, terkadang kodingan kita memiliki banyak eror. Mereka mungkin muncul dikarenakan kesalahan kita, input dari user yang tidak terduga, eror respon dari server, dan juga berbagai macam alasan lainnya.\n\nBiasanya, sebuah kodingan/scrip \"terhenti\" (tiba-tiba berhenti) dikarenakan adanya eror, menampilkan erornya pada console. \n\nTapi terdapat sebuah sintaks `try..catch` yang memperbolehkan kita untuk \"menangkap\" hasil eror sehingga skrip bisa berjalan sesuai dengan arahan kita, dibanding hanya berhenti saja.\n\n## Sintaks \"try..catch\"\n\nSintaks `try..catch` membentuk dua bagian utama: pertama `try`, dan kemudian `catch`:\n\n```js\ntry {\n\n  // kodingan disini\n\n} catch (err) {\n\n  // Penanganan jika eror\n\n}\n```\n\nMereka akan bekerja seperti ini:\n\n1. Pertama, kodingan pada `try {...}` akan dijalankan.\n2. Jika tidak terdapat eror, maka `catch(err)` akan dihiraukan: prosesnya akan mencapai ujung bagian `try` dan kemudian berlanjut, melewati bagian `catch`.\n3. Jika terdapat eror, maka bagian `try` akan berhenti berjalan, dan alur prosesnya akan berlanjut pada awal bagian `catch(err)`. Variabel `err` (yang mana kita bisa ganti dengan nama apapun) akan mengandung eror objek dengan keterangan eror didalamnya.\n\n![](try-catch-flow.svg)\n\nJadi, sebuah eror didalam bagian `try {…}` tidak akan memberhentikan kodingan tersebut -- kita memiliki sebuah kesempatan untuk menanganinya pada bagian `catch`.\n\nMari kita lihat contoh lainnya.\n\n- Sebuah contoh tanpa eror: menampilkan `alert` `(1)` dan `(2)`:\n\n    ```js run\n    try {\n\n      alert('Start of try runs');  // *!*(1) <--*/!*\n\n      // ...tidak ada eror disini\n\n      alert('End of try runs');   // *!*(2) <--*/!*\n\n    } catch (err) {\n\n      alert('Catch is ignored, because there are no errors'); // (3)\n\n    }\n    ```\n- Sebuah contoh dengan eror: shows `(1)` dan `(3)`:\n\n    ```js run\n    try {\n\n      alert('Start of try runs');  // *!*(1) <--*/!*\n\n    *!*\n      lalala; // error, variable is not defined!\n    */!*\n\n      alert('End of try (never reached)');  // (2)\n\n    } catch (err) {\n\n      alert(`Error has occurred!`); // *!*(3) <--*/!*\n\n    }\n    ```\n\n\n````warn header=\"`try..catch` hanya akan bekerja pada eror runtime\"\nUntuk `try..catch` agar bekerja, kodingan tersebut harus bisa dijalankan. Dengan artian lain, itu harus dalam bahasa javascript yang valid.\n\nMereka tidak akan bekerja jika kodingan tersebut secara sintaks salah, sebagai contoh jika mereka memiliki kurung kurawal yang tidak sama:\n\n\n```js run\ntry {\n  {{{{{{{{{{{{\n} catch (err) {\n  alert(\"The engine can't understand this code, it's invalid\");\n}\n```\nMesin Javascript pertama membaca kodingan tersebut, dan menjalankannya. eror yang terjadi pada saat proses pembacaan disebut sebagai eror \"parse-time\" dan tidak dapat dipulihkan (dari dalam kodingan tersebut). Itu dikarenakan mesin javascript tidak mengerti kodingan .\n\nJadi, `try..catch` hanya dapat menangani eror yang terjadi pada kodingan yang valid. eror demikian biasanya dinamakan sebagai \"eror runtime\" atau terkadang, \"exceptions\".\n````\n\n\n````warn header=\"`try..catch` bekerja secara sinkronis\"\nJika sebuah exception terjadi pada kodingan yang \"terjadwal\", seperti pada `setTimeout`, maka `try..catch` tidak akan menangkapnya: \n\n```js run\ntry {\n  setTimeout(function() {\n    noSuchVariable; // kodingan akan berhenti disini\n  }, 1000);\n} catch (err) {\n  alert( \"won't work\" );\n}\n```\n\nItu dikarenakan fungsi tersebut dijalankan nanti, ketika mesin javascript telah meninggalkan bagian pada `try..catch`.\n\nUntuk menangkap sebuah exception didalam sebuah fungsi yang terjadwal, `try..catch` harus terjadi didalam fungsi tersebut:\n```js run\nsetTimeout(function() {\n  try {    \n    noSuchVariable; // try..catch menangani eror tersebut!\n  } catch {\n    alert( \"error is caught here!\" );\n  }\n}, 1000);\n```\n````\n\n## Eror Objek\n\nKetika sebuah eror terjadi, Javascript menghasilkan sebuah ojek yang berisikan keterangan terkait eror tersebut. Objek itu kemudian dilewatkan sebagai sebuah argumen pada bagian `catch`:\n\n```js\ntry {\n  // ...\n} catch(err) { // <-- \"error object\", bisa menggunakan kata lain selain err\n  // ...\n}\n```\n\nUntuk semua eror bawaan, eror objek memiliki dua properti utama:\n\n`name`\n: Nama error. sebagai contoh, untuk variable yang belum terdefinisikan maka itu disebut `\"ReferenceError\"`.\n\n`message`\n: Pesan yang ada didalam eror tersebut.\n\nTerdapat properti non-standard lainnya pada kebanyakan \nAda properti non-standar lain yang tersedia di sebagian besar lingkungan. Salah satu yang paling banyak digunakan dan didukung ialah:\n\n`stack`\n: Call stack saat ini: string dengan informasi tentang urutan panggilan bertingkat yang menyebabkan kesalahan. Digunakan untuk tujuan debugging.\n\nSebagai contoh:\n\n```js run untrusted\ntry {\n*!*\n  lalala; // error, variable is not defined!\n*/!*\n} catch (err) {\n  alert(err.name); // ReferenceError\n  alert(err.message); // lalala is not defined\n  alert(err.stack); // ReferenceError: lalala is not defined at (...call stack)\n\n  // Can also show an error as a whole\n  // The error is converted to string as \"name: message\"\n  alert(err); // ReferenceError: lalala is not defined\n}\n```\n\n## \"Catch\" binding Opsional\n\n[recent browser=new]\n\nJika kita tidak butuh detail tentang eror, `catch` mungkin bisa menghilangkannya:\n\n```js\ntry {\n  // ...\n} catch { // <-- tanpa (err)\n  // ...\n}\n```\n\n## Menggunakan \"try..catch\"\n\nMari kita telusuri contoh penggunaan nyata dari `try..catch`.\n\nSeperti yang telah kita ketahui, Javascript mendukung method [JSON.parse(str)](mdn:js/JSON/parse) yang membaca dari nilai JSON-encoded. \n\nBiasanya digunakan untuk memecahkan kode data yang diterima melalui jaringan, dari server atau sumber lain.\n\nKita menerimanya dan memanggil `JSON.parse` seperti ini:\n\n```js run\nlet json = '{\"name\":\"John\", \"age\": 30}'; // data dari server\n\n*!*\nlet user = JSON.parse(json); // mengonversi representasi teks ke objek JS\n*/!*\n\n// sekarang pengguna adalah objek dengan properti dari string\nalert( user.name ); // John\nalert( user.age );  // 30\n```\n\nKalian dapat menemukan informasi lebih detail tentang JSON di bab <info: json>.\n\n**Jika format `json` salah,` JSON.parse` menghasilkan error, sehingga skrip \"mati\".**\n\nCukupkah kita puas dengan itu? Tentu saja tidak!\n\nDengan cara ini, jika ada yang salah dengan datanya, pengunjung tidak akan pernah mengetahuinya (kecuali mereka membuka konsol pengembang). Dan orang biasanya tidak suka ketika sesuatu \"berhenti begitu saja\" tanpa ada pesan kesalahan.\n\nMari gunakan `try..catch` untuk menangani kesalahan:\n\n```js run\nlet json = \"{ bad json }\";\n\ntry {\n\n*!*\n  let user = JSON.parse(json); // <-- ketika terjadi sebuah eror...\n*/!*\n  alert( user.name ); // tidak berjalan\n\n} catch (err) {\n*!*\n  // ...proses eksekusinya akan lompat kesini\n  alert( \"Our apologies, the data has errors, we'll try to request it one more time.\" );\n  alert( err.name );\n  alert( err.message );\n*/!*\n}\n```\nDi sini kita menggunakan blok `catch` hanya untuk menampilkan pesan, tetapi kita dapat melakukan lebih banyak lagi: mengirim permintaan jaringan baru, menyarankan alternatif kepada pengunjung, mengirim informasi tentang kesalahan ke fasilitas logging, .... Semuanya jauh lebih baik daripada sekedar mati.\n\n## Melontarkan eror kita sendiri\n\nBagaimana jika `json` secara sintaksis benar, tetapi tidak memiliki properti` name` yang diperlukan?\n\nSeperti ini:\n\n```js run\nlet json = '{ \"age\": 30 }'; // data tidak lengkap\n\ntry {\n\n  let user = JSON.parse(json); // <-- tidak ada eror\n*!*\n  alert( user.name ); // tidak ada nama!\n*/!*\n\n} catch (err) {\n  alert( \"doesn't execute\" );\n}\n```\nDi sini `JSON.parse` berjalan normal, tetapi tidak adanya` nama` sebenarnya merupakan eror bagi kita.\n\nUntuk menyatukan penanganan error, kita akan menggunakan operator `throw`.\n\n### Operator \"Throw\" \n\nOperator `throw` menghasilkan sebuah eror.\n\nSintaksnya adalah:\n\n```js\nthrow <error object>\n```\n\nSecara teknis, kita dapat menggunakan apapun sebagai eror objek. \nTechnically, we can use anything as an error object. Itu bahkan mungkin dengan data primitif, seperti angka atau string, lebih disarankan menggunakan objek, dan juga dengan properti `name` dan` message` (agar tetap kompatibel dengan error bawaan).\n\nJavaScript memiliki banyak konstruktor bawaan untuk standar eror: `Error`, `SyntaxError`,` ReferenceError`, `TypeError` dan lain-lain. Kita bisa menggunakannya untuk membuat eror objek juga.\n\nSintaksnya adalah:\n\n```js\nlet error = new Error(message);\n// or\nlet error = new SyntaxError(message);\nlet error = new ReferenceError(message);\n// ...\n```\n\nUntuk eror bawaan (bukan untuk objek lainnya, hanya untuk eror), properti `name` persis dengan nama konstruktornya. Dan `pesan` diambil dari argumennya.\n\nSebagai contoh:\n\n```js run\nlet error = new Error(\"Things happen o_O\");\n\nalert(error.name); // Error\nalert(error.message); // Things happen o_O\n```\n\nMari kita lihat jenis kesalahan apa yang dihasilkan `JSON.parse`:\n\n```js run\ntry {\n  JSON.parse(\"{ bad json o_O }\");\n} catch (err) {\n*!*\n  alert(err.name); // SyntaxError\n*/!*\n  alert(err.message); // Unexpected token b in JSON at position 2\n}\n```\n\nSeperti yang bisa kita lihat, itu adalah `SyntaxError`.\n\nDan dalam kasus ini, tidak adanya `nama` adalah sebuah eror, karena pengguna harus memiliki` nama`.\n\nJadi mari kita tampung pada bagian throw:\n\n```js run\nlet json = '{ \"age\": 30 }'; // data tidak lengkap\n\ntry {\n\n  let user = JSON.parse(json); // <-- tidak ada eror\n\n  if (!user.name) {\n*!*\n    throw new SyntaxError(\"Incomplete data: no name\"); // (*)\n*/!*\n  }\n\n  alert( user.name );\n\n} catch (err) {\n  alert( \"JSON Error: \" + err.message ); // JSON Error: Incomplete data: no name\n}\n```\n\nDi baris `(*)`, operator `throw` menghasilkan` SyntaxError` dengan `message` yang diberikan, sama seperti JavaScript akan menjalankannya sendiri. Proses eksekusi `try ` langsung berhenti dan alur kontrol melompat ke bagian ` catch`. \n\nSekarang `catch` menjadi satu tempat untuk semua penanganan error: baik untuk` JSON.parse` dan kasus lainnya.\n\n## Rethrowing\n\nPada contoh di atas kami menggunakan `try..catch` untuk menangani data yang salah. Tetapi apakah mungkin terjadi kesalahan tak terduga lainnya dalam blok `try {...}`? Seperti kesalahan pemrograman (variabel tidak terdefinisi) atau sesuatu yang lain, bukan hanya hal terkait \"data yang salah \" ini.\n\nSebagai contoh:\n\n```js run\nlet json = '{ \"age\": 30 }'; // data tidak lengkap\n\ntry {\n  user = JSON.parse(json); // <-- lupa meletakkan \"let\" sebelum user\n\n  // ...\n} catch (err) {\n  alert(\"JSON Error: \" + err); // JSON Error: ReferenceError: user is not defined\n  // (no JSON Error actually)\n}\n```\n\nTentu saja, semuanya bisa! Pemrogram memang membuat kesalahan. Bahkan dalam projek pembantu pada sumber terbuka(open source) yang digunakan oleh jutaan orang selama beberapa dekade - tiba-tiba bug dapat ditemukan yang mengarah ke peretasan yang mengerikan.\n\nDalam kasus kita, `try..catch` ditempatkan untuk menangkap eror `\"data yang tidak valid \"`. Tetapi pada dasarnya, `catch` mendapatkan *semua* error dari` try`. Di sini ia mendapat eror yang tak terduga, tetapi masih menampilkan pesan `\"JSON Error\"` yang sama. Itu salah dan juga membuat kode lebih sulit untuk di-debug.\n\nUntuk menghindari masalah seperti itu, kita dapat menggunakan teknik \"rethrowing\". Aturannya sederhana:\n\n**Proses throwing hanya akan memproses kesalahan yang diketahui dan \"melempar ulang / rethrowing\" yang lainnya.**\n\nTeknik \"rethrowing\" dapat dijelaskan lebih detail sebagai:\n\n1. Catch mendapatkan semua eror.\n2. Dalam blok `catch (err) {...}` kita menganalisis eror objek `err`.\n3. Jika kita tidak tahu bagaimana menanganinya, kita melakukan `throw err`.\n\nBiasanya, kita dapat memeriksa jenis erornya menggunakan operator `instanceof`:\n\n```js run\ntry {\n  user = { /*...*/ };\n} catch (err) {\n*!*\n  if (err instanceof ReferenceError) {\n*/!*\n    alert('ReferenceError'); // \"ReferenceError\" for accessing an undefined variable\n  }\n}\n```\n\nKita juga bisa mendapatkan nama kelas eror dari properti `err.name`. Semua eror bawaan memilikinya. Pilihan lainnya adalah membaca `err.constructor.name`.\n\nPada kode di bawah ini, kita menggunakan teknik rethrowing sehingga `catch` hanya menangani` SyntaxError`:\n\n```js run\nlet json = '{ \"age\": 30 }'; // incomplete data\ntry {\n\n  let user = JSON.parse(json);\n\n  if (!user.name) {\n    throw new SyntaxError(\"Incomplete data: no name\");\n  }\n\n*!*\n  blabla(); // unexpected error\n*/!*\n\n  alert( user.name );\n\n} catch (err) {\n\n*!*\n  if (err instanceof SyntaxError) {\n    alert( \"JSON Error: \" + err.message );\n  } else {\n    throw err; // rethrow (*)\n  }\n*/!*\n\n}\n```\n\nEror saat yang dilempar pada baris `(*)` dari dalam blok `catch` \"jatuh\" dari` try..catch` dan dapat ditangkap oleh bagian luar `try..catch`(jika ada), atau itu memberhentikan kodingannya.\n\nJadi, blok `catch` sebenarnya hanya menangani error yang tahu cara penanganannya dan \"melewatkan\" semua error lainnya.\n\nContoh di bawah ini menunjukkan bagaimana eror tersebut dapat ditangkap oleh satu level lagi dari blok `try..catch`:\n\n```js run\nfunction readData() {\n  let json = '{ \"age\": 30 }';\n\n  try {\n    // ...\n*!*\n    blabla(); // error!\n*/!*\n  } catch (err) {\n    // ...\n    if (!(err instanceof SyntaxError)) {\n*!*\n      throw e; // rethrow (tidak tahu bagaimana cara menanganinya)\n*/!*\n    }\n  }\n}\n\ntry {\n  readData();\n} catch (err) {\n*!*\n  alert( \"External catch got: \" + e ); // menangkapnya!\n*/!*\n}\n```\n\nDi sini `readData` hanya mengetahui cara menangani` SyntaxError`, sedangkan bagian `try..catch` luar mengetahui cara menangani semuanya.\n\n## try...catch...finally\n\nTunggu, itu belum semuanya.\n\nBlok `try..catch` mungkin memiliki satu klausa kode lagi yaitu:`finally`.\n\nJika ada, maka itu akan berjalan di semua kasus:\n\n- setelah `try`, jika tidak ada eror,\n- setelah `catch`, jika ada error.\n\nContoh sintaks lengkapnya seperti ini:\n\n```js\n*!*try*/!* {\n   ... try to execute the code ...\n} *!*catch*/!* (err) {\n   ... handle errors ...\n} *!*finally*/!* {\n   ... execute always ...\n}\n```\n\nTry running this code:\n\n```js run\ntry {\n  alert( 'try' );\n  if (confirm('Make an error?')) BAD_CODE();\n} catch (err) {\n  alert( 'catch' );\n} finally {\n  alert( 'finally' );\n}\n```\n\nKode tersebut memiliki dua cara eksekusi:\n\n1. Jika kalian menjawab \"Ya\" untuk \"Membuat eror?\", maka proses eksekusinya akan jadi seperti ini `try -> catch -> finally`.\n2. Jika kalian mengatakan \"Tidak\", maka proses nya akan seperti ini `try -> finally`.\n\nKlausa `finally` sering digunakan ketika kita mulai melakukan sesuatu dan ingin menyelesaikannya dalam kasus apa pun hasilnya.\n\nMisalnya, kami ingin mengukur waktu yang dibutuhkan oleh fungsi bilangan Fibonacci `fib (n)`. Secara alami, kita dapat mulai mengukur sebelum berlari dan menyelesaikannya setelahnya. Tetapi bagaimana jika ada kesalahan selama pemanggilan fungsi? Secara khusus, implementasi `fib (n)` dalam kode di bawah ini mengembalikan eror untuk bilangan negatif atau non-integer.\n\nKlausa `finally` adalah tempat yang tepat untuk menyelesaikan pengukuran apa pun yang terjadi.\n\nDi sini `finally` menjamin bahwa waktu akan diukur dengan benar dalam kedua situasi - jika eksekusi` fib` berhasil ataupun jika terjadi kesalahan di dalamnya:\n\n```js run\nlet num = +prompt(\"Enter a positive integer number?\", 35)\n\nlet diff, result;\n\nfunction fib(n) {\n  if (n < 0 || Math.trunc(n) != n) {\n    throw new Error(\"Must not be negative, and also an integer.\");\n  }\n  return n <= 1 ? n : fib(n - 1) + fib(n - 2);\n}\n\nlet start = Date.now();\n\ntry {\n  result = fib(num);\n} catch (err) {\n  result = 0;\n*!*\n} finally {\n  diff = Date.now() - start;\n}\n*/!*\n\nalert(result || \"error occurred\");\n\nalert( `execution took ${diff}ms` );\n```\n\nKalian dapat memeriksa dengan menjalankan kode dengan memasukkan `35` ke dalam` prompt` - ini dijalankan secara normal, `finally` setelah` try`. Dan kemudian masukkan `-1` - akan ada eror langsung, dan eksekusi akan memakan waktu` 0ms`. Kedua pengukuran tersebut dilakukan dengan benar.\n\nDengan kata lain, fungsi tersebut mungkin diakhiri dengan `return` atau `throw`, itu tidak masalah. Klausa `finally` dijalankan di kedua kasus.\n\n\n```smart header=\"Variables are local inside `try..catch..finally`\"\nTolong diperhatikan bahwa variabel `result` dan `diff` pada kode di atas dideklarasikan *sebelum* `try..catch`.\n\nSebaliknya, jika kita mendeklarasikan `let` di blok` try`, itu hanya akan terlihat di dalamnya.\n```\n\n````smart header=\"`finally` and `return`\"\nKlausa `finally` berfungsi untuk *apa saja* yang keluar dari` try..catch`. Itu termasuk `return` eksplisit.\n\nPada contoh di bawah ini, terdapat `return` dalam `try`. Dalam kasus ini, `finally` dijalankan tepat sebelum kontrol kembali ke kode luar.\n\n```js run\nfunction func() {\n\n  try {\n*!*\n    return 1;\n*/!*\n\n  } catch (err) {\n    /* ... */\n  } finally {\n*!*\n    alert( 'finally' );\n*/!*\n  }\n}\n\nalert( func() ); //pertama alert bekerja dari finally, dan kemudian yang satu ini\n```\n````\n\n````smart header=\"`try...finally`\"\n\nBagian `try..finally`, tanpa klausa` catch`, juga berguna. Kita menerapkannya ketika kita tidak ingin menangani eror di sini (biarkan eror itu terjadi), tetapi ingin memastikan bahwa proses yang kita mulai sudah selesai.\n\n```js\nfunction func() {\n  // mulai melakukan sesuatu yang perlu diselesaikan (seperti pengukuran)\n  try {\n    // ...\n  } finally {\n    // selesaikan itu bahkan jika semuanya berhenti\n  }\n}\n```\nPada kode di atas, eror di dalam `try` selalu terjadi, karena tidak ada `catch`. Tapi `finally` berfungsi sebelum aliran eksekusi meninggalkan fungsinya.\n````\n\n## Catch Global\n\n```warn header=\"Environment-specific\"\nInformasi dari bagian ini bukan merupakan bagian dari inti JavaScript.\n```\n\nBayangkan kita mendapatkan eror yang fatal di luar `try..catch`, dan kodingannya mati. Seperti eror pemrograman atau hal buruk lainnya.\n\nAdakah cara untuk bereaksi atas kejadian seperti itu? kita mungkin ingin mencatat kesalahan, menunjukkan sesuatu kepada pengguna (biasanya mereka tidak melihat pesan eror), dll.\n\nTidak ada dalam spesifikasinya, tapi dilingkungan tempat itu bekerja biasanya menyediakannya, karena sangat berguna. Misalnya, Node.js memiliki [`process.on (\" uncaughtException \")`] (https://nodejs.org/api/process.html#process_event_uncaughtexception) untuk itu. Dan pada browser kita dapat menetapkan fungsi ke properti khusus [window.onerror] (mdn: api / GlobalEventHandlers / onerror), yang akan berjalan jika terjadi kesalahan yang tidak tertangkap.\n\nSintaksnya adalah:\n\n```js\nwindow.onerror = function(message, url, line, col, error) {\n  // ...\n};\n```\n\n`message`\n: Pesan eror.\n\n`url`\n: URL pada kodingan tempat kesalahan terjadi.\n\n`line`, `col`\n: Nomor baris dan kolom ditempat terjadinya kesalahan.\n\n`error`\n: Eror objek\n\nSebagai contoh:\n\n```html run untrusted refresh height=1\n<script>\n*!*\n  window.onerror = function(message, url, line, col, error) {\n    alert(`${message}\\n At ${line}:${col} of ${url}`);\n  };\n*/!*\n\n  function readData() {\n    badFunc(); // Whoops, something went wrong!\n  }\n\n  readData();\n</script>\n```\n\nPeran dari global handler `window.onerror` biasanya bukan untuk memulihkan eksekusi dari kodingannya - itu biasanya tidak mungkin jika terjadi kesalahan pemrograman, namun tugasnya adalah untuk mengirim pesan eror ke pengembang.\n\nAda juga layanan web yang menyediakan pencatatan eror untuk kasus seperti itu, seperti <https://errorception.com> or <http://www.muscula.com>.\n\nMereka bekerja seperti ini:\n\n1. Kita mendaftarkannya di layanan dan mendapatkan sepotong JS (atau URL skrip) dari mereka untuk disisipkan di halaman.\n2. Skrip JS itu menyetel fungsi `window.onerror` kustom.\n3. Ketika terjadi kesalahan, itu mengirimkan permintaan jaringan tentangnya pada layanan itu.\n4. Kita dapat masuk ke antarmuka web layanan dan melihat erornya.\n\n## Kesimpulan\n\nBagian `try..catch` memungkinkan untuk menangani error runtime. Secara harfiah memungkinkan untuk \"mencoba\" menjalankan kode dan \"menangkap\" kesalahan yang mungkin terjadi di dalamnya.\n\nSintaksnya adalah:\n\n```js\ntry {\n  // Jalankan kode ini\n} catch(err) {\n  // jika terjadi kesalahan, lompat ke sini\n  // err adalah eror objek \n} finally {\n  // lakukan dalam hal apa pun setelah try / catch\n}\n```\n\nMungkin tidak ada bagian `catch` atau `finally`, jadi bagian yang lebih pendek `try..catch` dan` try..finally` juga valid.\n\nEror objek memiliki properti berikut ini:\n\n- `message` -- pesan kesalahan yang bisa dibaca manusia.\n- `name` -- string dengan nama eror (nama konstruktor eror).\n- `stack` (non-standar, tetapi didukung dengan baik) - tumpukan/stack pada saat pembuatan eror.\n\nJika eror objek tidak diperlukan, kita bisa menghilangkannya dengan menggunakan `catch {` daripada `catch (err) {`.\n\nKita juga bisa menghasilkan error kita sendiri menggunakan operator `throw`. Secara teknis, argumen dari `throw` bisa berupa apa saja, tetapi biasanya itu adalah eror objek yang diturunkan dari kelas ʻError` bawaan. Lebih lanjut tentang memperluas eror objek di bab berikutnya.\n\n*Rethrowing* adalah pola yang sangat penting dari penanganan eror: blok `catch` biasanya mengharapkan dan mengetahui bagaimana menangani jenis kesalahan tertentu, jadi blok tersebut harus menampilkan kembali kesalahan yang tidak diketahuinya.\n\nBahkan jika kita tidak memiliki `try..catch`, sebagian besar lingkungan memungkinkan kita menyiapkan penangan eror \"global\" untuk menangkap eror yang \"terjadi\". Di dalam browser, itu adalah `window.onerror`.\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/1-format-error/solution.md",
    "content": "```js run untrusted\nclass FormatError extends SyntaxError {\n  constructor(message) {\n    super(message);\n    this.name = this.constructor.name;\n  }\n}\n\nlet err = new FormatError(\"formatting error\");\n\nalert( err.message ); // formatting error\nalert( err.name ); // FormatError\nalert( err.stack ); // stack\n\nalert( err instanceof SyntaxError ); // true\n```\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/1-format-error/task.md",
    "content": "importance: 5\n\n---\n\n# Mewarisi dari SyntaxError\n\nBuat kelas `FormatError` yang diwarisi dari bawaan kelas `SyntaxError`.\n\nIni harus mendukung properti `message`, `name` dan `stack`.\n\nContoh penggunaan:\n\n```js\nlet err = new FormatError('formatting error');\n\nalert(err.message); // formatting error\nalert(err.name); // FormatError\nalert(err.stack); // stack\n\nalert(err instanceof FormatError); // true\nalert(err instanceof SyntaxError); // true (karena mewarisi dari SyntaxError)\n```\n"
  },
  {
    "path": "1-js/10-error-handling/2-custom-errors/article.md",
    "content": "# Kesalahan khusus, memperluas Kesalahan\n\nSaat kita mengembangkan sesuatu, kita sering membutuhkan kelas kesalahan kita sendiri untuk mencerminkan hal-hal spesifik yang mungkin salah dalam tugas kita. Untuk kesalahan dalam operasi jaringan kita mungkin memerlukan `HttpError`, untuk operasi basis data `DbError`, untuk operasi pencarian `NotFoundError` dan seterusnya.\n\nKesalahan kita harus mendukung properti kesalahan dasar seperti `message`, `name` dan, sebaiknya, `stack`. Tetapi mereka juga mungkin memiliki properti lain sendiri, misalnya objek `HttpError` mungkin memiliki properti `statusCode` dengan nilai seperti `404` atau `403` atau `500`.\n\nJavaScript memungkinkan untuk menggunakan `throw` dengan argumen apa pun, jadi secara teknis kelas kesalahan khusus kita tidak perlu mewarisi dari `Error`. Tetapi jika kita mewarisi, maka menjadi mungkin untuk menggunakan `obj instanceof Error` untuk mengidentifikasi objek kesalahan. Jadi lebih baik mewarisinya.\n\nSaat aplikasi berkembang, kesalahan kita sendiri secara alami membentuk hierarki. Misalnya, `HttpTimeoutError` mungkin mewarisi dari `HttpError`, dan seterusnya.\n\n## Memperluas Kesalahan\n\nSebagai contoh, mari pertimbangkan fungsi `readUser(json)` yang harus membaca JSON dengan data pengguna.\n\nBerikut adalah contoh tampilan `json` yang valid:\n\n```js\nlet json = `{ \"name\": \"John\", \"age\": 30 }`;\n```\n\nSecara internal, kita akan menggunakan `JSON.parse`. Jika menerima `json` yang salah, maka itu melontarkan `SyntaxError`. Tetapi bahkan jika `json` secara sintaksis benar, itu tidak berarti bahwa itu adalah pengguna yang valid, bukan? Ini mungkin kehilangan data yang diperlukan. Misalnya, mungkin tidak memiliki properti `name` dan `age` yang penting bagi pengguna kita.\n\nFungsi kita `readUser(json)` tidak hanya akan membaca JSON, tetapi juga memeriksa (\"memvalidasi\") data. Jika tidak ada bidang yang wajib diisi, atau formatnya salah, itu adalah kesalahan. Dan itu bukan `SyntaxError`, karena datanya benar secara sintaksis, tetapi jenis kesalahan lain. Kita akan menyebutnya `ValidationError` dan membuat kelas untuk itu. Kesalahan semacam itu juga harus membawa informasi tentang bidang yang melanggar.\n\nKelas `ValidationError` kita harus mewarisi dari kelas `Error` bawaan.\n\nKelas itu sudah ada di dalamnya, tetapi berikut ini kode perkiraannya sehingga kita dapat memahami apa yang kita perluas:\n\n```js\n// \"Kode semu\" untuk kelas Kesalahan bawaan yang ditentukan oleh JavaScript itu sendiri\nclass Error {\n  constructor(message) {\n    this.message = message;\n    this.name = \"Error\"; // (nama yang berbeda untuk kelas kesalahan bawaan yang berbeda)\n    this.stack = <call stack>; // tidak standar, tetapi sebagian besar lingkungan mendukungnya\n  }\n}\n```\n\nSekarang mari kita mewarisi `ValidationError` darinya dan mencobanya dalam tindakan:\n\n```js run untrusted\n*!*\nclass ValidationError extends Error {\n*/!*\n  constructor(message) {\n    super(message); // (1)\n    this.name = \"ValidationError\"; // (2)\n  }\n}\n\nfunction test() {\n  throw new ValidationError(\"Whoops!\");\n}\n\ntry {\n  test();\n} catch(err) {\n  alert(err.message); // Whoops!\n  alert(err.name); // ValidationError\n  alert(err.stack); // daftar panggilan bertingkat dengan masing-masing nomor baris\n}\n```\n\nHarap diperhatikan: pada baris `(1)` kita memanggil konstruktor induk. JavaScript mengharuskan kita memanggil `super` di konstruktor anak, jadi itu wajib. Konstruktor induk mengatur properti `message`.\n\nKonstruktor induk juga menyetel properti `name` menjadi `\"Error\"`, jadi di baris `(2)` kita menyetel ulang ke nilai yang benar.\n\nMari kita coba menggunakannya di `readUser(json)`:\n\n```js run\nclass ValidationError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n// Penggunaan\nfunction readUser(json) {\n  let user = JSON.parse(json);\n\n  if (!user.age) {\n    throw new ValidationError(\"No field: age\");\n  }\n  if (!user.name) {\n    throw new ValidationError(\"No field: name\");\n  }\n\n  return user;\n}\n\n// Contoh kerja dengan try..catch\n\ntry {\n  let user = readUser('{ \"age\": 25 }');\n} catch (err) {\n  if (err instanceof ValidationError) {\n*!*\n    alert(\"Invalid data: \" + err.message); // Invalid data: No field: name (Tidak ada bidang: name)\n*/!*\n  } else if (err instanceof SyntaxError) { // (*)\n    alert(\"JSON Syntax Error: \" + err.message);\n  } else {\n    throw err; // unknown error (kesalahan yang tidak diketahui), lontarkan kembali (**)\n  }\n}\n```\n\nBlok `try..catch` dalam kode di atas menangani baik `ValidationError` dan `SyntaxError` bawaan dari `JSON.parse`.\n\nSilakan lihat bagaimana kita menggunakan `instanceof` untuk memeriksa jenis kesalahan spesifik pada baris `(*)`.\n\nKita juga bisa melihat `err.name`, seperti ini:\n\n```js\n// ...\n// daripada (err instanceof SyntaxError)\n} else if (err.name == \"SyntaxError\") { // (*)\n// ...\n```\n\nVersi `instanceof` jauh lebih baik, karena di masa mendatang kita akan memperluas `ValidationError`, membuat subtipe darinya, seperti `PropertyRequiredError`. Dan pemeriksaan `instanceof` akan terus berfungsi untuk kelas pewaris baru. Jadi itu bukti masa depan.\n\nJuga penting bahwa jika `catch` menemui kesalahan yang tidak diketahui, maka itu akan ditarik kembali di baris `(**)`. Blok `catch` hanya mengetahui cara menangani validasi dan kesalahan sintaksis, jenis lain (karena kesalahan ketik pada kode atau kesalahan lain yang tidak diketahui) akan gagal.\n\n## Warisan lebih lanjut\n\nKelas `ValidationError` sangat umum. Banyak hal mungkin salah. Properti mungkin tidak ada atau mungkin dalam format yang salah (seperti nilai string untuk `age`). Mari kita buat kelas yang lebih konkret `PropertyRequiredError`, tepatnya untuk properti yang tidak ada. Ini akan membawa informasi tambahan tentang properti yang hilang.\n\n```js run\nclass ValidationError extends Error {\n  constructor(message) {\n    super(message);\n    this.name = \"ValidationError\";\n  }\n}\n\n*!*\nclass PropertyRequiredError extends ValidationError {\n  constructor(property) {\n    super(\"No property: \" + property);\n    this.name = \"PropertyRequiredError\";\n    this.property = property;\n  }\n}\n*/!*\n\n// Penggunaan\nfunction readUser(json) {\n  let user = JSON.parse(json);\n\n  if (!user.age) {\n    throw new PropertyRequiredError(\"age\");\n  }\n  if (!user.name) {\n    throw new PropertyRequiredError(\"name\");\n  }\n\n  return user;\n}\n\n// Contoh kerja dengan try..catch\n\ntry {\n  let user = readUser('{ \"age\": 25 }');\n} catch (err) {\n  if (err instanceof ValidationError) {\n*!*\n    alert(\"Invalid data: \" + err.message); // Invalid data: No property: name (Tidak ada properti: name)\n    alert(err.name); // PropertyRequiredError\n    alert(err.property); // name\n*/!*\n  } else if (err instanceof SyntaxError) {\n    alert(\"JSON Syntax Error: \" + err.message);\n  } else {\n    throw err; // unknown error (kesalahan yang tidak diketahui), lontarkan kembali\n  }\n}\n```\n\nKelas baru `PropertyRequiredError` mudah digunakan: kita hanya perlu meneruskan nama properti: `new PropertyRequiredError(property) `. `Pesan` yang dapat dibaca manusia dihasilkan oleh konstruktor.\n\nHarap dicatat bahwa `this.name` dalam konstruktor `PropertyRequiredError` ditetapkan lagi secara manual. Itu mungkin agak membosankan -- untuk menetapkan `this.name = <class name>` di setiap kelas kesalahan kustom. Kita dapat menghindarinya dengan membuat kelas \"kesalahan dasar\" kita sendiri yang menetapkan `this.name = this.constructor.name`. Dan kemudian mewarisi semua kesalahan khusus kita darinya.\n\nSebut saja `MyError`.\n\nBerikut kode dengan `MyError` dan kelas kesalahan khusus lainnya, yang disederhanakan:\n\n```js run\nclass MyError extends Error {\n  constructor(message) {\n    super(message);\n*!*\n    this.name = this.constructor.name;\n*/!*\n  }\n}\n\nclass ValidationError extends MyError { }\n\nclass PropertyRequiredError extends ValidationError {\n  constructor(property) {\n    super(\"No property: \" + property);\n    this.property = property;\n  }\n}\n\n// name-nya benar\nalert( new PropertyRequiredError(\"field\").name ); // PropertyRequiredError\n```\n\nSekarang kesalahan khusus jauh lebih pendek, terutama `ValidationError`, karena kita menyingkirkan baris `\"this.name = ...\"` di konstruktor.\n\n## Pengecualian pembungkusan\n\nTujuan dari fungsi `readUser` pada kode di atas adalah \"untuk membaca data pengguna\". Mungkin ada berbagai jenis kesalahan dalam prosesnya. Saat ini kita memiliki `SyntaxError` dan `ValidationError`, tetapi di masa mendatang fungsi `readUser` dapat berkembang dan mungkin menghasilkan jenis kesalahan lain.\n\nKode yang memanggil `readUser` harus menangani kesalahan ini. Saat ini ia menggunakan beberapa `if` dalam blok `catch`, yang memeriksa kelas dan menangani kesalahan yang diketahui dan memunculkan kembali yang tidak diketahui.\n\nSkemanya seperti ini:\n\n```js\ntry {\n  ...\n  readUser()  // potensi sumber kesalahan\n  ...\n} catch (err) {\n  if (err instanceof ValidationError) {\n    // menangani kesalahan validasi\n  } else if (err instanceof SyntaxError) {\n    // menangani kesalahan sintaks\n  } else {\n    throw err; // unknown error (kesalahan yang tidak diketahui), lontarkan kembali\n  }\n}\n```\n\nPada kode di atas kita dapat melihat dua jenis kesalahan, tetapi bisa juga lebih.\n\nJika fungsi `readUser` menghasilkan beberapa jenis kesalahan, maka kita harus bertanya pada diri sendiri: apakah kita benar-benar ingin memeriksa semua jenis kesalahan satu per satu setiap saat?\n\nSeringkali jawabannya adalah \"Tidak\": kita ingin menjadi \"satu tingkat di atas semua itu\". Kita hanya ingin tahu apakah ada \"kesalahan membaca data\" -- mengapa sebenarnya hal itu terjadi seringkali tidak relevan (pesan kesalahan menjelaskannya). Atau, lebih baik lagi, kita ingin mendapatkan cara untuk mendapatkan detail kesalahan, tetapi hanya jika kita perlu.\n\nTeknik yang kita jelaskan di sini disebut \"pengecualian pembungkusan\".\n\n1. Kita akan membuat kelas baru `ReadError` untuk merepresentasikan kesalahan \"membaca data\" yang umum.\n2. Fungsi `readUser` akan menangkap kesalahan pembacaan data yang terjadi di dalamnya, seperti `ValidationError` dan `SyntaxError`, dan sebagai gantinya menghasilkan `ReadError`.\n3. Objek `ReadError` akan menyimpan referensi ke kesalahan asli dalam properti `cause` -nya.\n\nKemudian kode yang memanggil `readUser` hanya perlu memeriksa `ReadError`, bukan untuk setiap jenis kesalahan pembacaan data. Dan jika membutuhkan detail lebih lanjut tentang kesalahan, ia dapat memeriksa properti `cause`-nya.\n\nBerikut kode yang mendefinisikan `ReadError` dan mendemonstrasikan penggunaannya dalam `readUser` dan `try..catch`:\n\n```js run\nclass ReadError extends Error {\n  constructor(message, cause) {\n    super(message);\n    this.cause = cause;\n    this.name = 'ReadError';\n  }\n}\n\nclass ValidationError extends Error { /*...*/ }\nclass PropertyRequiredError extends ValidationError { /* ... */ }\n\nfunction validateUser(user) {\n  if (!user.age) {\n    throw new PropertyRequiredError(\"age\");\n  }\n\n  if (!user.name) {\n    throw new PropertyRequiredError(\"name\");\n  }\n}\n\nfunction readUser(json) {\n  let user;\n\n  try {\n    user = JSON.parse(json);\n  } catch (err) {\n*!*\n    if (err instanceof SyntaxError) {\n      throw new ReadError(\"Syntax Error\", err);\n    } else {\n      throw err;\n    }\n*/!*\n  }\n\n  try {\n    validateUser(user);\n  } catch (err) {\n*!*\n    if (err instanceof ValidationError) {\n      throw new ReadError(\"Validation Error\", err);\n    } else {\n      throw err;\n    }\n*/!*\n  }\n\n}\n\ntry {\n  readUser('{bad json}');\n} catch (e) {\n  if (e instanceof ReadError) {\n*!*\n    alert(e);\n    // Original error: SyntaxError: Unexpected token b in JSON at position 1 (Kesalahan sintaks: Token b yang tidak terduga di JSON pada posisi 1)\n    alert(\"Original error: \" + e.cause);\n*/!*\n  } else {\n    throw e;\n  }\n}\n```\n\nPada kode di atas, `readUser` bekerja persis seperti yang dijelaskan -- menangkap kesalahan sintaks dan validasi dan melontarkan kesalahan `ReadError` (kesalahan yang tidak diketahui ditampilkan ulang seperti biasa).\n\nJadi kode terluar memeriksa `instanceof ReadError` dan hanya itu. Tidak perlu mencantumkan semua kemungkinan jenis kesalahan.\n\nPendekatan ini disebut \"pembungkusan pengecualian\", karena kita mengambil pengecualian \"tingkat rendah\" dan \"membungkusnya\" menjadi `ReadError` yang lebih abstrak. Ini banyak digunakan dalam pemrograman berorientasi objek.\n\n## Ringkasan\n\n- Kita bisa mewarisi dari `Error` dan kelas kesalahan bawaan lainnya secara normal. Kita hanya perlu menjaga properti `name` dan jangan lupa memanggil `super`.\n- Kita bisa menggunakan `instanceof` untuk memeriksa kesalahan tertentu. Ini juga bekerja dengan warisan. Namun terkadang kita memiliki objek kesalahan yang berasal dari pustaka pihak ketiga dan tidak ada cara mudah untuk mendapatkan kelasnya. Kemudian properti `name` dapat digunakan untuk pemeriksaan semacam itu.\n- Pengecualian pembungkusan adalah teknik yang tersebar luas: fungsi menangani pengecualian tingkat rendah dan membuat kesalahan tingkat lebih tinggi daripada berbagai kesalahan tingkat rendah. Pengecualian tingkat rendah terkadang menjadi properti dari objek tersebut seperti `err.cause` dalam contoh di atas, tetapi itu tidak sepenuhnya diperlukan.\n"
  },
  {
    "path": "1-js/10-error-handling/index.md",
    "content": "# Penanganan kesalahan\n"
  },
  {
    "path": "1-js/11-async/01-callbacks/article.md",
    "content": "\n\n# Pengenalan: callback\n\n```warn header=\"Disini kita menggunakan method dari browser\"\nUntuk menunjukkan penggunaan callback, promise dan konsep abstract lainnya, kita akan menggunakan beberapa method dari browser; khususnya, memuat script dan melakukan manipulasi dokumen sederhana.\n\nJika kamu belum terbiasa dengan method ini, dan penggunaanya didalam contoh membuat bingung, atau jika kamu hanya ingin mengerti lebih baik lagi,kamu mungkin mau membaca beberapa bab dari [bagian selanjutnya](/dokumen) tutorial ini.\n\nMeski, kita akan mencoba memperjelas situasi ini. Takkan ada yang sebijaksana browser komplex yang rumit.\n```\n\nBanyak action didalam JavaScript yang *asynchronous*. Dengan kata lain, kita inisiasi action tersebut sekarang, tetapi action tersebut selesai-nya nanti.\n\nSebagai contoh, kita bisa atur action tersebut menggunakan `setTimeout`.\n\nContoh-contoh lain dari action asynchronous di kehidupan nyata, misalnya memuat script dan module (kita akan bahas di bab selanjutnya).\n\nCoba lihat pada fungsi `loadScript(src)`, yang memuat sebuah script dengan pemberian `src`:\n\n```js\nfunction loadScript(src) {\n  // creates a <script> tag and append it to the page\n  // this causes the script with given src to start loading and run when complete\n  let script = document.createElement('script');\n  script.src = src;\n  document.head.append(script);\n}\n```\n\nFungsi tersebut menambahkan ke dokumen baru, dibuat secara dinamis, tag `<script src=\"…\">` dengan `src` yang diberikan. Browser kemudian secara otomatis memuat dan menjalankannya ketika lengkap.\n\nKita bisa menggunakan fungsi tersebut seperti ini:\n\n```js\n// memuat dan menjalankan script sesuai path yang diberikan\nloadScript('/my/script.js');\n```\n\nScript tersebut dijalankan secara \"asynchronously\", dimulai dengan memuatnya sekarang, namun dijalankan nanti, ketika fungsi tersebut sudah selesai.\n\nJika ada kode lain di bawah `loadScript(…)`, ia tidak akan menunggu sampai pemuatan script selesai.\n\n```js\nloadScript('/my/script.js');\n// kode lain dibawah loadScript\n// tidak akan menunggu sampai pemuatan script selesai\n// ...\n```\n\nKatakanlah kita butuh menggunakan script baru segera setelah dimuat. Mendeklarasikan fungsi baru, dan kita mau menjalankannya.\n\nTetapi jika kita melakukannya secara langsung setelah memanggil `loadScript(…)`, itu tidak akan berfungsi:\n\n```js\nloadScript('/my/script.js'); // script memiliki \"fungsi newFunction() {…}\"\n\n*!*\nnewFunction(); // tidak ada fungsi seperti itu!\n*/!*\n```\n\nTentu saja, browser mungkin tidak punya waktu untuk memuat script tersebut. Seperti yang sekarang, fungsi `loadScript` tidak menyediakan sebuah cara untuk melacak selesainya proses pemuatan. script dimuat dan akhirnya berjalan, itu saja. Tetapi kita ingin tahu ketika itu terjadi, untuk menggunakan fungsi baru dan variable dari script itu.\n\nMari tambahkan sebuah fungsi `callback` sebagai argumen kedua untuk `loadScript` yang seharusnya dijalankan ketika memuat script:\n\n```js\nfunction loadScript(src, *!*callback*/!*) {\n  let script = document.createElement('script');\n  script.src = src;\n\n*!*\n  script.onload = () => callback(script);\n*/!*\n\n  document.head.append(script);\n}\n```\n\nSekarang jika kita ingin memanggil fungsi baru dari script, kita harus menulisnya didalam callback:\n\n```js\nloadScript('/my/script.js', function() {\n  // callback berjalan setelah script dimuat\n  newFunction(); // jadi sekarang bisa berfungsi\n  ...\n});\n```\n\nItu idenya: argumen kedua adalah sebuah fungsi (biasanya anonymous) yang berjalan ketika sebuah action selesai.\n\nIni contoh yang bisa dijalankan dengan script asli:\n\n```js run\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n  script.onload = () => callback(script);\n  document.head.append(script);\n}\n\n*!*\nloadScript('https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.2.0/lodash.js', script => {\n  alert(`Cool, the ${script.src} is loaded`);\n  alert( _ ); // fungsi dideklarasi di dalam script yang dimuat\n});\n*/!*\n```\n\nItu disebut gaya \"callback-based\" dalam pemrograman asynchronous. Sebuah fungsi yang melakukan sesuatu secara asynchronous harus menyediakan sebuah argumen `callback` dimana kita meletakkan fungsi untuk dijalankan setelah selesai.\n\nDi sini kita melakukannya dalam `loadScript`, tapi tentu saja ini pendekatan secara umum.\n\n## Callback didalam callback\n\nBagaimana kita bisa memuat dua script secara berurutan: yang pertama, dan kemudian setelahnya yang kedua?\n\nSolusi alaminya bisa kita letakkan `loadScript` kedua kemudian panggil didalam callback, seperti ini:\n\n```js\nloadScript('/my/script.js', function(script) {\n\n  alert(`Keren, ${script.src} sudah dimuat, ayo muat satu lagi`);\n\n*!*\n  loadScript('/my/script2.js', function(script) {\n    alert(`Keren, script kedua sudah dimuat`);\n  });\n*/!*\n\n});\n```\n\nSetelah `loadScript` yang diluar sudah selesai, kemudian callback inisiasi yang didalam.\n\nBagaimana jika kita ingin menambahkan satu script lagi...?\n\n```js\nloadScript('/my/script.js', function(script) {\n\n  loadScript('/my/script2.js', function(script) {\n\n*!*\n    loadScript('/my/script3.js', function(script) {\n      // ...dilanjutkan setelah semua script sudah dimuat\n    });\n*/!*\n\n  });\n\n});\n```\n\nJadi, setiap action baru ada didalam callback. Tidak masalah untuk beberapa action, tetapi tidak bagus apabila banyak action, jadi kita akan melihat varian lainnya.\n\n## Menangani error\n\nPada contoh diatas kita tidak mempertimbangkan error. Bagaimana jika script yang dimuat gagal? Callback kita harusnya bisa bereaksi terhadap itu.\n\nIni sebuah versi `loadScript` yang ditingkatkan untuk melacak error saat memuat:\n\n```js\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n\n*!*\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Error memuat script untuk ${src}`));\n*/!*\n\n  document.head.append(script);\n}\n```\n\nItu akan memanggil `callback(null, script)` apabila berhasil memuat dan `callback(error)` jika tidak berhasil.\n\nPengunaanya:\n```js\nloadScript('/my/script.js', function(error, script) {\n  if (error) {\n    // menangani error\n  } else {\n    // sukses memuat script\n  }\n});\n```\n\nSekali lagi, resep yang kita gunakan untuk `loadScript` sebenarnya cukup umum. Itu disebut gaya \"error-first callback\".\n\nKetentuan-nya adalah:\n1. Argumen pertama dari `callback` dicadangkan untuk sebuah error jika itu terjadi. Kemudian `callback(err)` dipanggil.\n2. Argumen kedua (dan argumen selanjutnya jika dibutuhkan) untuk hasil yang sukses. Kemudian `callback(null, result1, result2…)` dipanggil.\n\nJadi fungsi `callback` single tersebut keduanya digunakan untuk pelaporan error dan memberikan sebuah hasil.\n\n## Pyramid of Doom\n\nDari tampilan pertama, itu adalah cara yang layak untuk coding secara asynchronous. Dan memang demikian. Untuk satu atau bahkan dua pemanggilan bersarang itu tidak masalah.\n\nTetapi untuk beberapa action asynchronous yang mengikuti satu demi satu kita akan punya kode seperti ini:\n\n```js\nloadScript('1.js', function(error, script) {\n\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', function(error, script) {\n      if (error) {\n        handleError(error);\n      } else {\n        // ...\n        loadScript('3.js', function(error, script) {\n          if (error) {\n            handleError(error);\n          } else {\n  *!*\n            // ...dilanjutkan setelah semua script dimuat (*)\n  */!*\n          }\n        });\n\n      }\n    });\n  }\n});\n```\n\nDalam kode di atas:\n1. Kita memuat `1.js`, kemudian jika tidak ada error.\n2. Kita muat `2.js`, kemudian jika tidak ada error.\n3. Kita muat `3.js`, jika tidak ada error -- lakukan sesuatu yang lain `(*)`.\n\nSaat pemanggilan jadi lebih bersarang, kode akan menjadi lebih dalam dan semakin susah untuk diatur, khususnya jika kita punya kode asli daripada `...`, itu mungkin berisi loop yang lebih, pernyataan bersyarat dan seterusnya.\n\nIni kadang disebut \"callback hell\" atau \"pyramid of doom.\"\n\n<!--\nloadScript('1.js', function(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', function(error, script) {\n      if (error) {\n        handleError(error);\n      } else {\n        // ...\n        loadScript('3.js', function(error, script) {\n          if (error) {\n            handleError(error);\n          } else {\n            // ...\n          }\n        });\n      }\n    });\n  }\n});\n-->\n\n![](callback-hell.svg)\n\n\"Piramida\" panggilan bersarang bergerak ke kanan dengan setiap action asynchronous. Segera itu akan lepas kendali.\n\nJadi, ini merupakan cara coding yang tidak baik.\n\nKita bisa coba untuk meringankan masalah tersebut dengan membuat setiap action menjadi fungsi yang mandiri atau berdiri sendiri, seperti ini:\n\n```js\nloadScript('1.js', step1);\n\nfunction step1(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('2.js', step2);\n  }\n}\n\nfunction step2(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...\n    loadScript('3.js', step3);\n  }\n}\n\nfunction step3(error, script) {\n  if (error) {\n    handleError(error);\n  } else {\n    // ...berlanjut setelah semua script dimuat(*)\n  }\n}\n```\n\nLihat? Itu sama saja, dan tidak ada sarang yang dalam sekarang karena kita buat setiap action menjadi fungsi top-level yang terpisah.\n\nIni berfungsi, tetapi kode-nya terlihat seperti sebuah spreadsheet yang terkoyak. Itu sulit di baca, dan kamu mungkin memperhatikan kalau yang satu butuh untuk eye-jump antara potongan lainnya saat di baca. Itu tidak nyaman, khususnya jika pembaca tidak terbiasa dengan kode dan tidak tahu harus kemana saat eye-jump.\n\nJuga, fungsi yang bernama `step*` semuanya digunakan sekali saja, mereka dibuat hanya untuk menghindari \"pyramid of doom.\" Tidak ada satupun yang digunakan kembali di luar rantai action. Jadi ada sedikit namespace yang berantakan disini.\n\nKita ingin memiliki sesuatu yang lebih baik.\n\nUntunglah, ada cara lain untuk menghindari pyramida seperti itu. Salah satu cara yang bagus yaitu menggunakan \"promise,\" di jelaskan dalam bab selanjutnya.\n"
  },
  {
    "path": "1-js/11-async/01-callbacks/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/01-re-resolve/solution.md",
    "content": "Keluarannya adalah: `1`.\n\nPanggilan kedua untuk `resolve` diabaikan, karena hanya panggilan pertama `reject/resolve` yang diperhitungkan. Panggilan lebih lanjut diabaikan.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/01-re-resolve/task.md",
    "content": "\n# Selesaikan ulang sebuah promise?\n\n\nApa keluaran dari kode di bawah ini? \n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  resolve(1);\n\n  setTimeout(() => resolve(2), 1000);\n});\n\npromise.then(alert);\n```\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/02-delay-promise/solution.md",
    "content": "```js run\nfunction delay(ms) {\n  return new Promise(resolve => setTimeout(resolve, ms));\n}\n\ndelay(3000).then(() => alert('berjalan setelah 3 detik'));\n```\n\nHarap dicatat bahwa penyelesaian tugas ini, `resolve` dipanggil tanpa argumen. Kita tidak mengembalikkan nilai apapun dari `delay`, hanya memastikan penundaan tersebut.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/02-delay-promise/task.md",
    "content": "\n# Tunda dengan promise\n\nFungsi bawaan `setTimeout` menggunakan *callbacks*. Buat alternatif berbasis *promise*.\n\nFungsi `delay(ms)` harus mengembalikkan sebuah *promise*. *Promise* itu harus diselesaikan setelah `ms` milidetik, sehingga kita bisa menambahkan `.then` ke fungsi tersebut, seperti ini:\n\n```js\nfunction delay(ms) {\n  // kode kamu\n}\n\ndelay(3000).then(() => alert('runs after 3 seconds'));\n```\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.md",
    "content": ""
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .message-ball {\n      font-size: 20px;\n      line-height: 200px;\n      text-align: center;\n    }\n    .circle {\n      transition-property: width, height, margin-left, margin-top;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"go()\">Click me</button>\n\n  <script>\n\n  function go() {\n    showCircle(150, 150, 100).then(div => {\n      div.classList.add('message-ball');\n      div.append(\"Hello, world!\");\n    });\n  }\n\n  function showCircle(cx, cy, radius) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    return new Promise(resolve => {\n      setTimeout(() => {\n        div.style.width = radius * 2 + 'px';\n        div.style.height = radius * 2 + 'px';\n\n        div.addEventListener('transitionend', function handler() {\n          div.removeEventListener('transitionend', handler);\n          resolve(div);\n        });\n      }, 0);\n    })\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/03-animate-circle-promise/task.md",
    "content": "\n# Lingkaran animasi dengan promise\n\nTulis ulang fungsi `showCircle` di dalam solusi tugas <info:task/animate-circle-callback> sehingga fungsi tersebut mengembalikan sebuah *promise* daripada menerima sebuah *callback*.\n\nPenggunaan baru:\n\n```js\nshowCircle(150, 150, 100).then(div => {\n  div.classList.add('message-ball');\n  div.append(\"Hello, world!\");\n});\n```\n\nAmbil solusi pada tugas <info:task/animate-circle-callback> sebagai dasar.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/article.md",
    "content": "# Promise\n\n\nBayangkan kamu adalah seorang penyanyi top, dan penggemarmu bertanya siang dan malam untuk *single* terbarumu.\n\nUntuk mendapatkan kelegaan, kamu berjanji untuk mengirimkan *single* tersebut kepada mereka ketika diterbitkan. Kamu memberikan sebuah daftar kepada penggemarmu. Mereka dapat mengisi alamat surel mereka, sehingga saat lagu sudah tersedia, semua pihak yang berlangganan langsung menerimanya. Dan bahkan jika ada yang salah, katakanlah, ada kebakaran di dalam studio, sehingga kamu tidak dapat menerbitkan lagu, mereka masih akan diberitahu.\n\nSemua orang senang: kamu, karena orang-orang tidak memadati kamu lagi, dan penggemar, karena mereka tidak ketinggalan *single*nya.\n\n1. \"Kode produksi\" itu melakukan sesuatu dan membutuhkan waktu. Sebagai contoh, sebuah kode yang memuat data melalui jaringan. Itu adalah seorang \"penyanyi\".\n2. \"Kode pengkonsumsi\" yang menginginkan hasil dari \"kode produksi\" setelah siap. Banyak fungsi yang mungkin membutuhkan hasil itu. Ini adalah \"penggemar\".\n3. *Promise* adalah objek Javascript khusus yang menghubungkan \"kode produksi\" dan \"kode pengkonsumi\" secara bersamaan. Dalam analogi kami: ini adalah \"daftar berlangganan\". \"Kode produksi\" membutuhkan waktu berapa pun untuk menghasilkan hasil yang dijanjikan, dan \"*Promise*\" membuat hasil tersebut tersedia untuk semua kode yang berlangganan ketika hasilnya sudah siap.\n\nAnalogi ini tidak terlalu akurat, karena *promise* JavaScript lebih kompleks dari daftar berlangganan sederhana: daftar tersebut memiliki fitur dan batasan tambahan. Tetapi untuk awal tidak apa-apa.\n\n*Syntax constructor* untuk objek *promise* adalah:\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // eksekutor (kode produksi, \"penyanyi\")\n});\n```\n\nFungsi yang dilewatkan ke `new Promise` disebut sebagai *exekutor*. Ketika `new Promise` dibuat, exekutor tersebut berjalan secara otomatis. Exekutor itu berisi kode produksi, yang pada akhirnya harus memproduksi hasil. Dalam analogi di atas: exekutor adalah \"penyanyi\".\n\nArgumen `resolve` dan `reject` adalah *callback* yang disediakan oleh JavaScript itu sendiri. Kode kita hanya ada di dalam eksekutor.\n\n\nKetika eksekutor mendapatkan hasilnya, baik itu cepat atau lambat - tidak masalah, eksekutor harus memanggil salah satu dari *callback* ini:\n\n- `resolve(value)` — jika pekerjaan selesai dengan sukses, dengan hasil `value`.\n- `reject(error)` — jika terjadi kesalahan, `error` adalah objek kesalahan.\n\n\nJadi untuk meringkas: eksekutor berjalan secara otomatis, eksekutor harus melakukan pekerjaan dan kemudian memanggil salah satu dari `resolve` atau `reject`.\n\nObjek `promise` yang dikembalikan oleh *constructor* `new Promise` memiliki properti internal:\n\n- `state` — pada awalnya `\"pending\"`, kemudian berubah menjadi `\"fulfilled\"` saat `resolve` dipanggil atau `\"rejected\"` ketika `reject` dipanggil.\n- `result` — pada awalnya `undefined`, kemudian berubah menjadi `value` ketika `resolve(value)` dipanggil atau `error` ketika `reject(error)` dipanggil.\n\nJadi eksekutor akhirnya memindahkan `promise` ke salah satu dari kondisi ini:\n\n![](promise-resolve-reject.svg)\n\nNanti kita akan melihat bagaimana \"penggemar\" dapat berlangganan kepada perubahan ini.\n\nBerikut ini contoh *constructor promise* dan fungsi eksekutor sederhana dengan \"kode produksi\" yang membutuhkan waktu (melalui `setTimeout`):\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  // fungsi tersebut dieksekusi secara otomatis ketika \"promise\" dibangun\n\n  // setelah 1 detik menandakan bahwa pekerjaan selesai dengan hasil \"done\"\n  setTimeout(() => *!*resolve(\"done\")*/!*, 1000);\n});\n```\n\nKita dapat melihat dua hal dengan menjalankan kode di atas:\n\n1. Exekutor dipanggil secara langsung dan otomatis (oleh `new Promise`).\n2. Exekutor menerima dua argumen: `resolve` dan `reject` — fungsi ini sudah ditentukan sebelumnya oleh mesin JavaScript. Jadi kita tak perlu membuatnya. Kita hanya harus memanggil salah satu dari dua argumen tersebut ketika siap.\n\n    Setelah satu detik \"memproses\" eksekutor memanggil `resolve(\"done\")` untuk memproduksi hasilnya. Ini mengubah status objek `promise`:\n\n    ![](promise-resolve-1.svg)\n\nItu adalah contoh penyelesaian pekerjaan yang sukses, sebuah \"*promise fulfilled*\".\n\nDan sekarang adalah contoh eksekutor menolak *promise* dengan sebuah *error*:\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // setelah 1 detik menandakan bahwa pekerjaan selesai dengan sebuah \"error\"\n  setTimeout(() => *!*reject(new Error(\"Whoops!\"))*/!*, 1000);\n});\n```\n\nPanggilan untuk `reject(...)` memindahkan objek *promise* ke status `\"rejected\"`:\n\n![](promise-reject-1.svg)\n\nUntuk meringkas, eksekutor harus melakukan pekerjaan (sesuatu yang biasanya membutuhkan waktu) dan kemudian memanggil `resolve` atau `reject` untuk mengubah state objek *promise* yang sesuai.\n\n*Promise* yang diputuskan atau ditolak disebut \"diselesaikan\", sebagai lawan dari *promise* \"pending\" awalnya.\n\n````smart header=\"Hanya ada satu hasil atau sebuah 'error'\"\nEksekutor harus memanggil hanya satu `resolve` atau satu `reject`. Setiap perubahan status adalah final.\n\nSemua panggilan `resolve` dan `reject` lebih lanjut diabaikan:\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n*!*\n  resolve(\"done\");\n*/!*\n\n  reject(new Error(\"…\")); // diabaikan\n  setTimeout(() => resolve(\"…\")); // diabaikan\n});\n```\n\nIdenya adalah bahwa pekerjaan yang dilakukan oleh eksekutor mungkin hanya memiliki satu hasil atau *error*.\n\nJuga, `resolve`/`reject` hanya berharap satu argumen (atau tidak ada) dan akan mengabaikan argumen tambahan.\n````\n\n```smart header=\"Reject dengan objek `Error`\"\nSeandainya terjadi kesalahan, eksekutor harus memanggil `reject`. Itu bisa dilakukan dengan segala jenis argumen (seperti `resolve`). Tetapi direkomendasikan untuk menggunakan objek `Error` (atau objek yang mewarisi dari `Error`). Alasannya akan segera menjadi jelas.\n```\n\n````smart header=\"Memanggil langsung `resolve`/`reject`\"\nDalam praktiknya, eksekutor biasanya melakukan sesuatu secara *asynchronous* dan memanggil `resolve`/`reject` setelah beberapa waktu, tetapi tidak harus. Kita juga bisa memanggil `resolve` atau `reject` secara langsung, seperti ini:\n\n```js\nlet promise = new Promise(function(resolve, reject) {\n  // tidak mengambil waktu kita untuk melakukan pekerjaan itu\n  resolve(123); // secara langsung memberikan hasil: 123\n});\n```\n\nMisalnya, ini mungkin terjadi ketika kita memulai suatu pekerjaan tetapi kemudian melihat segalanya sudah selesai dan di-*cache*.\n\nTidak apa-apa. Kita segera menyelesaikan *promise*.\n````\n\n```smart header=\"`State` dan `result` bersifat internal\"\nProperti `state` dan `result`    objek Promise bersifat internal. Kita tidak bisa mengakses properti tersebut secara langsung. Kita bisa menggunakan *method* `.then`/`.catch`/`.finally` untuk melakukannya. Penjelasan method-method tersebut ada di bawah ini.\n```\n\n## Konsumen: then, catch, finally\n\nObjek *Promise* berfungsi sebagai tautan antara eksekutor (\"kode produksi\" atau \"penyanyi\") dan fungsi konsumsi (\"penggemar\"), yang akan menerima hasil atau *error*. Fungsi konsumsi bisa didaftarkan (berlangganan) menggunakan *method* `.then`, `.catch` and `.finally`.\n\n### then\n\nYang paling penting, yang mendasar adalah `.then`.\n\n*Syntax*nya adalah:\n\n```js\npromise.then(\n  function(result) { *!*/* menangani hasil yang sukses */*/!* },\n  function(error) { *!*/* menangani sebuah \"error\" */*/!* }\n);\n```\n\nArgumen pertama dari `.then` adalah fungsi yang berjalan ketika *promise* terselesaikan, dan menerima hasil.\n\nArgumen kedua dari `.then` adalah fungsi yang berjalan ketika *promise* ditolak, dan menerima *error*.\n\nSebagai contoh, disini reaksi ketika *promise* berhasil diselesaikan:\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  setTimeout(() => resolve(\"done!\"), 1000);\n});\n\n// resolve menjalankan fungsi pertama di .then\npromise.then(\n*!*\n  result => alert(result), // menampilkan \"done!\" setelah satu detik\n*/!*\n  error => alert(error) // tidak dijalankan\n);\n```\n\nFungsi pertama dijalankan.\n\nDan dalam hal penolakan -- yang kedua:\n\n```js run\nlet promise = new Promise(function(resolve, reject) {\n  setTimeout(() => reject(new Error(\"Whoops!\")), 1000);\n});\n\n// reject menjalankan fungsi kedua di .then\npromise.then(\n  result => alert(result), // tidak dijalankan\n*!*\n  error => alert(error) // menampilkan \"Error: Whoops!\" setelah satu detik\n*/!*\n);\n```\n\nJika kita hanya tertarik pada penyelesaian yang berhasil, maka kita hanya dapat menyediakan satu argumen fungsi `.then`:\n\n```js run\nlet promise = new Promise(resolve => {\n  setTimeout(() => resolve(\"done!\"), 1000);\n});\n\n*!*\npromise.then(alert); // menampilkan \"done!\" setelah satu detik\n*/!*\n```\n\n### catch\n\nJika kita hanya tertarik pada *error*, maka kita dapat menggunakan `null` sebagai argumen pertama: `.then(null, errorHandlingFunction)`. Atau kita dapat menggunakan `.catch(errorHandlingFunction)`, yang mana keduanya sama persis:\n\n\n```js run\nlet promise = new Promise((resolve, reject) => {\n  setTimeout(() => reject(new Error(\"Whoops!\")), 1000);\n});\n\n*!*\n// .catch(f) sama seperti promise.then(null, f)\npromise.catch(alert); // menampilkan \"Error: Whoops!\" setelah satu detik\n*/!*\n```\n\nPanggilan `.catch(f)` adalah analog lengkap dari `.then(null, f)`, itu hanya sebuah singkatan.\n\n### finally\n\nSama seperti ada klausa `finally` dalam `try {...} catch {...}`, ada `finally` dalam *promises*.\n\nPanggilan `.finally(f)` mirip dengan `.then(f, f)` dalam arti bahwa `f` selalu berjalan ketika *promise* diselesaikan: apakah itu *resolve* atau *reject*.\n\n`finally` adalah penanganan yang baik untuk melakukan pembersihan, mis. menghentikan indikator pemuatan kita, karena tidak diperlukan lagi, apa pun hasilnya.\n\nSeperti ini:\n\n```js\nnew Promise((resolve, reject) => {\n  /* lakukan sesuatu yang membutuhkan waktu, dan kemudian panggil resolve/reject */\n})\n*!*\n\n  // berjalan ketika \"promise\" diselesaikan, tidak peduli sukses atau tidak\n  .finally(() => hentikan indikator pemuatan)\n*/!*\n  .then(result => munculkan hasil, err => munculkan \"error\")\n```\n\n\nTapi ini bukan alias dari `then(f,f)`. Ada beberapa perbedaan penting:\n\n\n1. *Handler* `finally` tidak memiliki argumen. Didalam `finally` kita tidak tahu apakah *promise* sukses atau tidak. Tidak apa-apa, karena tugas kita biasanya melakukan prosedur penyelesaian \"umum\".\n2. *Handler* `finally` melewatkan hasil dan *error* ke *handler* selanjutnya.\n\n    Misalnya, di sini hasilnya dilewatkan melalui `finally` ke `then`:\n    ```js run\n    new Promise((resolve, reject) => {\n      setTimeout(() => resolve(\"result\"), 2000)\n    })\n      .finally(() => alert(\"Promise ready\"))\n      .then(result => alert(result)); // <-- .then menangani hasilnya\n    ```\n\n    Dan di sini ada *error* di dalam *promise*, dilewatkan melalui `finally` ke `catch`:\n\n    ```js run\n    new Promise((resolve, reject) => {\n      throw new Error(\"error\");\n    })\n      .finally(() => alert(\"Promise ready\"))\n      .catch(err => alert(err));  // <-- .catch menangani objek galat\n    ```  \n\n\n    Itu sangat nyaman, karena `finally` tidak dimaksudkan untuk memproses hasil dari *promise*. Jadi itu melewatinya.\n\n    Kita akan berbicara lebih banyak tentang *chaining promise* dan *passing-result* antara *handler* di bab selanjutnya.\n\n3. Terakhir, namun tidak kalah pentingnya, *syntax* `.finally(f)` lebih nyaman daripada `.then(f, f)`: tidak perlu menduplikasi fungsi `f`.\n\n````smart header=\"Dengan promise yang sudah ditentukan handler segera menjalankannya\"\n Jika *promise* tertunda, *handler* `.then/catch/finally` akan menunggu *promise* tersebut. Jika tidak, jika *promise* sudah selesai, handler langsung menjalankan:\n\n```js run\n// \"promise\" diselesaikan segera setelah dibuat\nlet promise = new Promise(resolve => resolve(\"done!\"));\n\npromise.then(alert); // done! (muncul sekarang)\n```\nPerhatikan bahwa ini membuat Promise lebih efektif daripada skenario \"daftar berlangganan\" di kehidupan nyata. Jika penyanyi sudah merilis lagu mereka dan kemudian seseorang mendaftar di daftar berlangganan, mereka mungkin tidak akan menerima lagu itu. Berlangganan dalam kehidupan nyata harus dilakukan sebelum acara tersebut.\n\nPromise lebih fleksibel. Kita bisa menambahkan handlers kapan saja: jika hasilnya sudah ada, mereka langsung mengeksekusi.\n````\n\nSelanjutnya, mari kita lihat contoh-contoh yang lebih praktis tentang bagaimana *promise* dapat membantu kita menulis kode *asynchronous*.\n\n## Contoh: loadScript [#loadscript]\n\nKita punya fungsi `loadScript` untuk memuat skrip dari bab sebelumnya.\n\nInilah varian berbasis *callback*, hanya untuk mengingatkan kita tentang itu:\n\n```js\nfunction loadScript(src, callback) {\n  let script = document.createElement('script');\n  script.src = src;\n\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Script load error for ${src}`));\n\n  document.head.append(script);\n}\n```\n\nMari tulis ulang menggunakan *Promise*.\n\nFungsi baru `loadScript` tidak akan memerlukan *callback*. Sebagai gantinya, fungsi tersebut akan membuat dan mengembalikkan sebuah objek *Promise* yang diselesaikan ketika pemuatan sudah selesai. Kode yang paling luar dapat menambah *handler* (fungsi berlangganan) dengan menggunakan `.then`:\n\n```js run\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(`Script load error for ${src}`));\n\n    document.head.append(script);\n  });\n}\n```\n\nPemakaian:\n\n```js run\nlet promise = loadScript(\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js\");\n\npromise.then(\n  script => alert(`${script.src} is loaded!`),\n  error => alert(`Error: ${error.message}`)\n);\n\npromise.then(script => alert('Another handler...'));\n```\n\nKita dapat segera melihat beberapa manfaat melalui pola berbasis *callback*:\n\n\n| Promise | Callback |\n|----------|-----------|\n| *Promise* memungkinkan kita melakukan hal-hal dalam urutan alami. Pertama, kita menjalankan `loadScript(script)`, dan `.then` kita menulis apa yang harus dilakukan dengan hasilnya. | Kita harus punya fungsi `callback` yang kita miliki saat memanggil `loadScript(script, callback)`. Dengan kata lain, kita harus tau apa yang harus dilakukan dengan hasil *sebelum* `loadScript` dipanggil. |\n| Kita dapat memanggil `.then` pada *Promise* sebanyak yang kita inginkan. Setiap kali, kita tambahkan \"fan\" baru, fungsi berlangganan baru, ke \"daftar berlangganan\". Lebih lanjut tentang ini di bab selanjutnya: [](info:promise-chaining). | Hanya ada satu *callback*. |\n\nJadi *promise* memberikan kita aliran kode dan fleksibilitas yang lebih baik. Tetapi masih ada lagi. Kita akan melihatnya di bab-bab selanjutnya.\n"
  },
  {
    "path": "1-js/11-async/02-promise-basics/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/01-then-vs-catch/solution.md",
    "content": "Jawaban singkatnya adalah: **tidak, mereka tidak sama**:\n\nPerbedaannya adalah bahwa jika terjadi sebuah _error_ di dalam `f1`, kemudian ditangani oleh `.catch` disini:\n\n```js run\npromise.then(f1).catch(f2);\n```\n\n...Tetapi bukan disini:\n\n```js run\npromise.then(f1, f2);\n```\n\nItulah kenapa sebuah _error_ diturunkan ke _chain_, dan didalam bagian kode kedua disana tidak ada _chain_ dibawah `f1`.\n\n\nDengan kata lain, `.then` meneruskan _result_/_error_ ke `.then/catch` selanjutnya. Jadi pada contoh pertama, ada sebuah `catch` di bawah, dan yang kedua -- disana tidak ada, jadi _error_ tidak ditangani.\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/01-then-vs-catch/task.md",
    "content": "# Promise: then versus catch\n\nApakah potongan kode ini sama? Dengan kata lain, apakah mereka berperilaku sama dalam situasai apapun, untuk setiap _handler functions_?\n\n```js\npromise.then(f1).catch(f2);\n```\n\nVersus:\n\n```js\npromise.then(f1, f2);\n```\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/article.md",
    "content": "# Promises chaining\n\n\nMari kembali ke masalah yang disebutkan di dalam bab <info:callbacks>: kita memiliki sebuah urutan tugas _asynchronous_ untuk dilakukan satu demi satu. Sebagai contoh, memuat _scripts_. Bagaimana kita bisa membuat kodenya dengan baik?\n\nPromises menyediakan beberapa resep untuk melakukannya.\n\nDi bab ini kita melibatkan _promise chaining_.\n\nItu terlihat seperti ini:\n\n```js run\nnew Promise(function (resolve, reject) {\n  setTimeout(() => resolve(1), 1000); // (*)\n})\n  .then(function (result) {\n    // (**)\n\n    alert(result); // 1\n    return result * 2;\n  })\n  .then(function (result) {\n    // (***)\n\n    alert(result); // 2\n    return result * 2;\n  })\n  .then(function (result) {\n    alert(result); // 4\n    return result * 2;\n  });\n```\n\nIdenya adalah bahwa **result** diteruskan melalui rantai _handlers_ `.then`.\n\nIni alurnya:\n\n1. Promise pertama selesai dalam 1 detik `(*)`,\n2. Kemudian _handler_ `.then` dipanggil `(**)`.\n3. Nilai yang dikembalikan diteruskan ke handler `.then` selanjutnya `(***)`\n4. ...dan seterusnya.\n\nSelama **result** diteruskan di sepanjang rantai _handlers_, kita bisa melihat urutan pemanggilan `alert`: `1` -> `2` -> `4`.\n\n![](promise-then-chain.svg)\n\nSeluruhnya bekerja, karena pemanggilan ke `promise.then` mengembalikan sebuah _promise_, jadi kita bisa memanggil `.then` selanjutnya.\n\nKetika sebuah _handler_ mengembalikan nilai, _handler_ tersebut menjadi hasil dari promise, jadi `.then` selanjutnya dipanggil dengan itu.\n\n**Kesalahan klasik pemula: secara teknis kita juga dapat menambahkan banyak `.then` ke satu promise. Ini bukan chaining.**\n\nSebagai contoh:\n\n```js run\nlet promise = new Promise(function (resolve, reject) {\n  setTimeout(() => resolve(1), 1000);\n});\n\npromise.then(function (result) {\n  alert(result); // 1\n  return result * 2;\n});\n\npromise.then(function (result) {\n  alert(result); // 1\n  return result * 2;\n});\n\npromise.then(function (result) {\n  alert(result); // 1\n  return result * 2;\n});\n```\n\nApa yang kita lakukan di sini hanya beberapa _handlers_ untuk satu _promise_. _Handlers_ tersebut tidak meneruskan result ke satu sama lain, melainkan memprosesnya masing-masing.\n\nIni gambarnya (bandingkan dengan _chaining_ di atas):\n\n![](promise-then-many.svg)\n\nSemua `.then` pada _promise_ yang sama mendapatkan _result_ yang sama -- _result_ dari _promise_. Jadi di dalam kode di atas semua `alert` menunjukkan yang sama: `1`.\n\nDalam prakteknya kita jarang membutuhkan banyak _handlers_ untuk satu _promise_. Chaining lebih sering digunakan.\n\n## Mengembalikan promises\n\nSebuah _handler_, yang digunakan di dalam `.then(handler)` dapat membuat dan mengambalikan sebuah _promise_.\n\nDalam hal ini _handlers_ selanjutnya menunggu sampai mengendap, dan kemudian mendapatkan hasilnya.\n\nSebagai contoh:\n\n```js run\nnew Promise(function(resolve, reject) {\n\n  setTimeout(() => resolve(1), 1000);\n\n}).then(function(result) {\n\n  alert(result); // 1\n\n*!*\n  return new Promise((resolve, reject) => { // (*)\n    setTimeout(() => resolve(result * 2), 1000);\n  });\n*/!*\n\n}).then(function(result) { // (**)\n\n  alert(result); // 2\n\n  return new Promise((resolve, reject) => {\n    setTimeout(() => resolve(result * 2), 1000);\n  });\n\n}).then(function(result) {\n\n  alert(result); // 4\n\n});\n```\n\nDi sini `.then` pertama menunjukan `1` dan mengembalikan `new Promise(…)` pada baris `(*)`. Setelah satu detik selesai, dan hasil (argument `resolve`, di sini `result * 2`) diteruskan ke _handler_ `.then` kedua. Handler pada baris `(**)`, menunjukan `2` dan melakukan hal yang sama.\n\nJadi keluarannya sama dengan contoh sebelumnya: 1 -> 2 -> 4, tetapi sekarang dengan menunda 1 detik antara pemanggilan `alert`.\n\nMengembalikan _promises_ memperbolehkan kita untuk membangun rantai aksi _asynchronous_.\n\n## Contoh: loadScript\n\nMari menggunakan fitur ini dengan _promisified_ `loadScript`, didefinisikan di [bab sebelumnya](info:promise-basics#loadscript), untuk memuat _scripts_ satu demi satu, secara berurutan:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\")\n  .then(function (script) {\n    return loadScript(\"/article/promise-chaining/two.js\");\n  })\n  .then(function (script) {\n    return loadScript(\"/article/promise-chaining/three.js\");\n  })\n  .then(function (script) {\n    // gunakan functions yang dideklarasikan di dalam scripts\n    // untuk menunjukkan bahwa functions memang dimuat\n    one();\n    two();\n    three();\n  });\n```\n\nKode ini bisa lebih ringkas dengan _arrow functions_:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\")\n  .then((script) => loadScript(\"/article/promise-chaining/two.js\"))\n  .then((script) => loadScript(\"/article/promise-chaining/three.js\"))\n  .then((script) => {\n    // scripts dimuat, kita dapat menggunakan functions yang dideklarasikan di sini\n    one();\n    two();\n    three();\n  });\n```\n\nDi sini setiap pemanggilan `loadScript` mengembalikkan sebuah _promise_, dan `.then` selanjutnya berjalan ketika _promise_ selesai. Kemudian memulai pemuatan _script_ selanjutnya. Jadi _scripts_ dimuat satu setelah yang lain.\n\n\nKita dapat menambahkan lagi aksi asynchronous ke rantainya. Harap catat bahwa kodenya masih \"flat\", kodenya tumbuh ke bawah bukan ke kanan. Tidak ada tanda-tanda \"pyramid of doom\".\n\nSecara teknis, kita dapat menambahkan `.then` secara langsung ke setiap `loadScript`, seperti ini:\n\n```js run\nloadScript(\"/article/promise-chaining/one.js\").then((script1) => {\n  loadScript(\"/article/promise-chaining/two.js\").then((script2) => {\n    loadScript(\"/article/promise-chaining/three.js\").then((script3) => {\n      // this function has access to variables script1, script2 and script3\n      one();\n      two();\n      three();\n    });\n  });\n});\n```\n\nKode ini melakukan hal yang sama: muat 3 _scripts_ berurutan. Tetapi \"tumbuh ke kanan\". Jadi kita punya masalah yang sama dengan _callbacks_.\n\nOrang yang baru memulai untuk menggunakan _promises_ kadang-kadang tidak tahu tentang _chaining_, jadi mereka menulisnya dengan cara ini. Umumnya, _chaining_ lebih disukai.\n\nTerkadang ok untuk menulis `.then` secara langsung, karena _function_ bersarang memiliki akses ke luar _scope_. Pada contoh di atas _callback_ paling bertingkat memiliki akses ke semua variabel `script1`, `script2`, `script3`. Tetapi itu pengecualian bukan aturan.\n\n````smart header=\"Thenables\"\n\nTepatnya, sebuah handler mungkin tidak mengembalikkan sebuah promise, tetapi dipanggil objek \"thenable\" - sebuah objek sewenang-wenang yang memiliki method `.then`, dan diperlakukan sama seperti sebuah promise.\n\nIdenya adalah bahwa pustaka 3rd-party dapat menerapkan objek \"promise-compatible\" mereka sendiri. Mereka dapat memiliki serangkaian methods yang luas, tetapi juga kompatibel dengan promises asli, karena mereka menerapkan `.then`.\n\n\nIni contoh dari objek thenable:\n\n```js run\nclass Thenable {\n  constructor(num) {\n    this.num = num;\n  }\n  then(resolve, reject) {\n    alert(resolve); // function() { native code }\n    // selesai dengan this.num*2 setelah 1 detik\n    setTimeout(() => resolve(this.num * 2), 1000); // (**)\n  }\n}\n\nnew Promise(resolve => resolve(1))\n  .then(result => {\n*!*\n    return new Thenable(result); // (*)\n*/!*\n  })\n  .then(alert); // menunjukkan 2 setelah 1000ms\n```\n\n\nJavaScript memeriksa objek yang dikembalikkan oleh handler `.then` di baris `(*)`: jika ada method callable yang bernama `then`, kemudian method tersebut memanggil method yang menyediakan functions `resolve`, `reject` asli sebagai arguments (mirip ke eksekutor) dan menunggu sampai satu dari mereka dipanggil. Pada contoh di atas `resolve(2)` dipanggil setelah 1 detik `(**)`. Kemudian result diteruskan ke bawah chain.\n\n\nFitur ini memperbolehkan kita untuk untuk mengintegrasikan objek kustom dengan promise chains tanpa memiliki pewarisan dari `Promise`.\n````\n\n## Contoh Terbesar: fetch\n\nDi dalam pemrograman frontend promises sering digunakan untuk permintaan jaringan. Jadi mari lihat contoh yang lebih luas dari itu.\n\nKita akan menggunakan methos [fetch](info:fetch) untuk memuat informasi tentang pengguna dari server jarak jauh. Banyak sekali pilihan parameter yang dilibatkan di dalam [bab terpisah](info:fetch), tetapi sintaksis dasar cukup sederhana:\n\n```js\nlet promise = fetch(url);\n```\n\nIni membuat permintaan jaringan ke `url` dan mengembalikkan sebuah _promise_. Promise selesai dengan objek `response` ketika server jarak jauh merespon dengan header, tetapi _sebelum response penuh diunduh_.\n\n\nUntuk membaca response penuh, kita harus memanggil sebuah method `response.text()`: method tersebut mengembalikkan sebuah promise yang selesai ketika teks penuh ull telah diunduh dari server jarak jauh, dengan teks tersebut sebagai hasilnya.\n\nKode di bawah ini membuat permintaan ke `user.json` dan memuat teks dari server:\n\n```js run\nfetch(\"/article/promise-chaining/user.json\")\n  // .then di bawah berjalan ketika server jarak jauh merespon\n  .then(function (response) {\n    // response.text() mengembalikkan sebuah promise baru yang selesai dengan response teks penuh\n    // ketika dimuat\n    return response.text();\n  })\n\n  .then(function (text) {\n    // ...dan di sini isi dari file remote\n    alert(text); // {\"name\": \"iliakan\", isAdmin: true}\n  });\n```\n\nDi sana juga ada method `response.json()` yang membaca data remote dan parsing sebagai JSON. Pada kasus kita lebih sesuai, jadi mari ganti dengan itu.\n\n  .then(function(text) {\n    // ...and here's the content of the remote file\n    alert(text); // {\"name\": \"iliakan\", \"isAdmin\": true}\n  });\n```\n\nKita juga akan menggunakan arrow functions untuk keringkasan:\n\n```js run\n// sama seperti di atas, tetapi response.json() parsing konten remote sebagai JSON\nfetch(\"/article/promise-chaining/user.json\")\n  .then((response) => response.json())\n  .then((user) => alert(user.name)); // iliakan, mendapatkan user name\n```\n\nSekarang mari lakukan sesuatu dengan memuat pengguna.\n\nSebagai contoh, kita dapat membuat satu atau lebih permintaan ke GitHub, muat profil pengguna dan tunjukkan avatarnya:\n\n```js run\n// Buat permintaan ke user.json\nfetch(\"/article/promise-chaining/user.json\")\n  // Muat sebagai json\n  .then((response) => response.json())\n  // Buat permintaan ke GitHub\n  .then((user) => fetch(`https://api.github.com/users/${user.name}`))\n  // Muat response sebagai json\n  .then((response) => response.json())\n  // Tunjukkan gambar avatar (githubUser.avatar_url) untuk 3 detik (mungkin hidupkan itu)\n  .then((githubUser) => {\n    let img = document.createElement(\"img\");\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => img.remove(), 3000); // (*)\n  });\n```\n\nKodenya bekerja, lihat komentar tentang detail. Meskipun, di sana ada potensi masalah di dalamnya, kesalahan umum dari mereka yang mulai menggunakan promise.\n\nLihat pada baris `(*)`: bagaimana kita dapat melakukan sesuatu _setelah_ avatar telah muncul dan dihapus? sebagai contoh, kita ingin menunjukkan form untuk mengubah pengguna atau sesuatu yang lain. Sampai sekarang, tidak mungkin.\n\nUntuk membuat chain bisa diperpanjang, kita butuh untuk mengembalikkan sebuah promise yang selesai ketika avatar selesai muncul.\n\nSeperti ini:\n\n```js run\nfetch('/article/promise-chaining/user.json')\n  .then(response => response.json())\n  .then(user => fetch(`https://api.github.com/users/${user.name}`))\n  .then(response => response.json())\n*!*\n  .then(githubUser => new Promise(function(resolve, reject) { // (*)\n*/!*\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n*!*\n      resolve(githubUser); // (**)\n*/!*\n    }, 3000);\n  }))\n  // terpicu setelah 3 detik\n  .then(githubUser => alert(`Finished showing ${githubUser.name}`));\n```\n\nItu adalah, _handler_ `.then` pada baris `(*)` sekarang mengembalikkan `new Promise`, yang menjadi mengendap hanya setelah pemanggilan `resolve(githubUser)` dalam `setTimeout` `(**)`.\n\n`.then` selanjutnya di dalam _chain_ akan menunggu untuk itu.\n\nSeperti peraturan yang bagus, sebuah aksi _asynchronous_ harus selalu mengembalikkan sebuah promise.\n\nItu membuat kemungkinan untuk rencana aksi setelahnya. Bahkan jika kita tidak berencana memperpanjang _chain_ sekarang, kita mungkin membutuhkannya nanti.\n\nAkhinya, kita dapat memecah kodenya ke dalam _function_ yang dapat digunakan kembali:\n\n```js run\nfunction loadJson(url) {\n  return fetch(url).then((response) => response.json());\n}\n\nfunction loadGithubUser(name) {\n  return fetch(`https://api.github.com/users/${name}`).then((response) =>\n    response.json()\n  );\n}\n\nfunction showAvatar(githubUser) {\n  return new Promise(function (resolve, reject) {\n    let img = document.createElement(\"img\");\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n      resolve(githubUser);\n    }, 3000);\n  });\n}\n\n// Gunakan mereka:\nloadJson(\"/article/promise-chaining/user.json\")\n  .then((user) => loadGithubUser(user.name))\n  .then(showAvatar)\n  .then((githubUser) => alert(`Finished showing ${githubUser.name}`));\n// ...\n```\n\n## Ringkasan\n\nJika ada _handler_ `.then` (atau `catch/finally`, tidak masalah) mengembalikkan sebuah _promise_, _chain_ sisanya akan menunggu sampai mengendap. Saat itu terjadi, hasilnya (atau error) diteruskan lebih jauh.\n\nDi sini gambar penuhnya:\n\n![](promise-handler-variants.svg)\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/getMessage.js",
    "content": "function getMessage() {\n  return \"Hello, world!\";\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n\n<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/three.js",
    "content": "function three() {\n  alert(3);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/03-promise-chaining/user.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/01-error-async/solution.md",
    "content": "Jawabannya adalah: **tidak, tidak terpicu**:\n\n```js run\nnew Promise(function (resolve, reject) {\n  setTimeout(() => {\n    throw new Error(\"Whoops!\");\n  }, 1000);\n}).catch(alert);\n```\n\nSeperti yang dikatakan di bab, ada sebuah \"`try..catch` implisit\" di sekitar kode function. jadi semua error synchronous ditangani.\n\nTetapi di sini error tersebut tidak dihasilkan saat eksekutornya berjalan, tapi nanti. Jadi promise tidak dapat menanganinya.\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/01-error-async/task.md",
    "content": "# Error di dalam setTimeout\n\nApa yang anda pikirkan? Akankan `.catch` terpicu? Jelaskan jawaban anda.\n\n```js\nnew Promise(function (resolve, reject) {\n  setTimeout(() => {\n    throw new Error(\"Whoops!\");\n  }, 1000);\n}).catch(alert);\n```\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/article.md",
    "content": "# Penanganan error dengan promise\n\nChain promise bagus dalam penanganan _error_. ketika sebuah promise me-reject, kontrolnya melompat ke handler rejection terdekat. Itu sangat nyaman dalam praktiknya.\n\nSebagai contoh, kode di bawah sebuah URL ke `fetch` itu salah (tidak ada situs seperti itu) dan `.catch` menangani error tersebut:\n\n```js run\n*!*\nfetch('https://no-such-server.blabla') // reject\n*/!*\n  .then(response => response.json())\n  .catch(err => alert(err)) // TypeError: failed to fetch (gagal mengambil resourse, error yang dihasilkan mungkin berbeda)\n```\n\nSeperti yang anda lihat, `.catch` tidak harus segera. `.catch` mungkin muncul setelah satu atau mungkin beberapa `.then`.\n\nAtau, mungkin, semuanya baik-baik saja dengan situs tersebut, tetapi response-nya bukan JSON yang valid. Cara termudah untuk catch semua _error_ adalah menambahkan `.catch` pada akhiran chain:\n\n```js run\nfetch('/article/promise-chaining/user.json')\n  .then(response => response.json())\n  .then(user => fetch(`https://api.github.com/users/${user.name}`))\n  .then(response => response.json())\n  .then(githubUser => new Promise((resolve, reject) => {\n    let img = document.createElement('img');\n    img.src = githubUser.avatar_url;\n    img.className = \"promise-avatar-example\";\n    document.body.append(img);\n\n    setTimeout(() => {\n      img.remove();\n      resolve(githubUser);\n    }, 3000);\n  }))\n*!*\n  .catch(error => alert(error.message));\n*/!*\n```\n\nBiasanya, `.catch` semacam itu tidak memicu sama sekali. Tetapi jika salah satu promise di atas me-reject (sebuah masalah jaringan atau json yang tidak valid atau apapun itu), maka promise tersebut akan meng-catch-nya.\n\n## try..catch implisit\n\nKode dari sebuah eksekutor promise dan handler promise memiliki \"`try..catch` yang tak terlihat\" di sekitarnya. Jika terjadi pengecualian, maka pengecualian itu tertangkap dan diperlakukan sebagai rejection.\n\nSebagai contoh, kode ini:\n\n```js run\nnew Promise((resolve, reject) => {\n*!*\n  throw new Error(\"Whoops!\");\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\n...Bekerja persis sama seperti ini:\n\n```js run\nnew Promise((resolve, reject) => {\n*!*\n  reject(new Error(\"Whoops!\"));\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\n\"`try..catch` yang tak terlihat\" di sekitar eksekutor secara otomatis menangkap _error_ dan mengubahnya menjadi promise yang direject.\n\nIni terjadi bukan hanya di dalam function eksekutor saja, tetapi di handler-nya juga. Jika kita `throw` dalam handler `.then`, itu artinya sebuah promise yang direject, jadi kontrolnya melompat ke handler error terdekat.\n\nIni contohnya:\n\n```js run\nnew Promise((resolve, reject) => {\n  resolve(\"ok\");\n}).then((result) => {\n*!*\n  throw new Error(\"Whoops!\"); // reject promise tersebut\n*/!*\n}).catch(alert); // Error: Whoops!\n```\n\nIni terjadi pada semua _error_, tidak hanya disebabkan oleh pernyataan `throw`. Sebagai contoh, sebuah _error_ pemrograman:\n\n```js run\nnew Promise((resolve, reject) => {\n  resolve(\"ok\");\n}).then((result) => {\n*!*\n  blabla(); // tidak ada fungsi seperti ini\n*/!*\n}).catch(alert); // ReferenceError: blabla is not defined (blabla tidak terdefinisi)\n```\n\n`.catch` terakhir tidak hanya meng-catch rejection secara ekplisit, tetapi juga sesekali _error_ dalam handler di atas.\n\n## Melempar kembali\n\nSeperti yang sudah kita perhatikan, `.catch` di akhir chain mirip dengan `try..catch`. Kita bisa saja memiliki sebanyak mungkin handler `.then` seperti yang kita inginkan, dan kemudian menggunakan satu `.catch` di akhir untuk menangani _error_ di semuanya.\n\nDi dalam `try..catch` biasa kita dapat menganalisis _error_ nya dan mungkin melemparkannya kembali jika tidak bisa ditangani. Hal yang sama mungkin untuk promise.\n\nJika kita `throw` di dalam `.catch`, kemudian kontrolnya mengarah ke handler _error_ terdekat selanjutnya. Dan jika kita menangani _error_ dan selesai dengan normal, kemudian berlanjut ke handler `.then` sukses terdekat berikutnya.\n\nPada contoh di bawah ini `.catch` sukses menangani _error_:\n\n```js run\n// eksekusi: catch -> then\nnew Promise((resolve, reject) => {\n  throw new Error(\"Whoops!\");\n})\n  .catch(function (error) {\n    alert(\"The error is handled, continue normally\");\n  })\n  .then(() => alert(\"Next successful handler runs\"));\n```\n\nDisini blok `.catch` selesai secara normal. Jadi handler `.then` yang sukses selanjutnya dipanggil.\n\nPada contoh di bawah ini kita lihat situasi lain dengan `.catch`. Handler `(*)` menangkap _error_ dan tidak bisa mengatasinya (misalnya hanya tahu bagaimana menangani `URIError`), jadi _error_-nya dilempar lagi:\n\n```js run\n// eksekusi: catch -> catch -> then\nnew Promise((resolve, reject) => {\n\n  throw new Error(\"Whoops!\");\n\n}).catch(function(error) { // (*)\n\n  if (error instanceof URIError) {\n    // tangani di sini\n  } else {\n    alert(\"Can't handle such error\");\n\n*!*\n    throw error; // lemparkan ini atau error lain melompat ke catch selanjutnya\n*/!*\n  }\n\n}).then(function() {\n  /* tidak berjalan di sini */\n}).catch(error => { // (**)\n\n  alert(`The unknown error has occurred: ${error}`);\n  // tidak mengembalikkan apapun => eksekusi berjalan normal\n\n});\n```\n\nEksekusi tersebut melompat dari `.catch` `(*)` pertama ke yang selanjutnya `(**)` menuruni chain.\n\n## Rejection yang tidak tertangani\n\nApa yang terjadi ketika sebuah _error_ tidak ditangani? Sebagai contoh, kita lupa untuk menambahkan `.catch` ke akhir chain, seperti ini:\n\n```js untrusted run refresh\nnew Promise(function () {\n  noSuchFunction(); // Error di sini (tidak ada function seperti itu)\n}).then(() => {\n  //  handler promise yang sukses, satu atau lebih\n}); // tanpa .catch di akhir!\n```\n\nJika terjadi _error_, promise jadi direject, dan eksekusi harus melompat ke handler penolakan terdekat. Tapi tidak ada. Jadi _error_-nya \"stuck\". Tidak ada kode untuk menangani-nya.\n\nDalam prakteknya, seperti _error_ biasa yang tidak tertangani dalam kode, itu berarti ada sesuatu yang tidak beres.\n\nApa yang terjadi ketika sebuah _error_ biasa muncul dan tidak tertangkap oleh `try..catch`? script-nya mati dengan sebuah pesan di console. Sesuatu yang mirip terjadi dengan rejection promise yang tidak tertangani.\n\nMesin JavaScript melacak rejection tersebut dan menghasilkan _error_ global dalam kasus itu. Anda dapat melihatnya di console jika anda menjalankan contoh di atas.\n\nDi dalam peramban kita dapat meng-catch kesalahan tersebut menggunakan event `unhandledrejection`:\n\n```js run\n*!*\nwindow.addEventListener('unhandledrejection', function(event) {\n  // object event tersebut memiliki dua properti spesial:\n  alert(event.promise); // [object Promise] - promise yang menghasilkan error\n  alert(event.reason); // Error: Whoops! - object error yang tidak tertangani\n});\n*/!*\n\nnew Promise(function() {\n  throw new Error(\"Whoops!\");\n}); // tidak ada catch untuk menangani error\n```\n\nEvent tersebut adalah bagian dari [standar HTML](https://html.spec.whatwg.org/multipage/webappapis.html#unhandled-promise-rejections).\n\nJika sebuah _error_ muncul, dan di sana tidak ada `.catch`, handler `unhandledrejection` terpicu, dan mendapatkan object `event` dengan informasi tentang _error_, jadi kita dapat melakukan sesuatu.\n\nBiasanya _error_ seperti itu tidak dapat dipulihkan, jadi jalan keluar terbaik kita adalah memberi tahu pengguna tentang masalah dan mungkin melaporkan insiden tersebut ke server.\n\nDi dalam lingkungan non-peramban seperti Node.js di sana ada cara lain untuk melacak _error_ yang tak tertangani.\n\n## Ringkasan\n\n- `.catch` menangani _error_ di segala macam promise: baik itu panggilan `reject()`, atau sebuah _error_ yang dilemparkan di dalam handler.\n- Kita harus meletakkan `.catch` tepat di tempat dimana kita ingin menangani _error_ dan tahu bagaimana untuk menangani _error-error_ tersebut. Handler harus menganalisa _error_ (bantuan class _error_ khusus) dan melemparkan kembali yang tidak diketahui (mungkin itu adalah kesalahan pemrograman).\n- Ok untuk tidak menggunakan `.catch` sama sekali, jika tidak ada cara untuk memulihkan dari kesalahan.\n- Bagaimanapun kita harus memiliki handler event `unhandledrejection` (untuk peramban,dan analog untuk lingkungan lainnya), untuk melacak _error_ yang tak tertangani dan memberi tahu pengguna (dan mungkin server kita) tentang _error-error_ tersebut, jadi aplikasi kita tidak pernah \"mati begitu saja\".\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/getMessage.js",
    "content": "function getMessage() {\n  return \"Hello, world!\";\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/head.html",
    "content": "<script>\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = 'HttpError';\n    this.response = response;\n  }\n}\n\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => {\n      if (response.status == 200) {\n        return response.json();\n      } else {\n        throw new HttpError(response);\n      }\n    })\n}\n</script>\n\n<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/three.js",
    "content": "function three() {\n  alert(3);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/04-promise-error-handling/user.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/article.md",
    "content": "# API Promise\n\nAda 6 method static di dalam class `Promise`. Kita akan segera membahas kasus penggunaan method-method tersebut di sini.\n\n## Promise.all\n\nKatakanlah kita ingin menjalankan banyak promise untuk dieksekusi secara paralel, dan menunggu sampai semua promise tersebut siap.\n\nSebagai contoh, unduh beberapa URL secara paralel dan proses isinya ketika semuanya selesai.\n\nItulah gunanya `Promise.all`.\n\nSintaksis nya adalah:\n\n```js\nlet promise = Promise.all([...promises...]);\n```\n\n`Promise.all` mengambil sebuah array promise (secara teknis bisa menjadi iterable, tetapi biasanya sebuah array) dan mengembalikkan promise baru.\n\nPromise baru resolve ketika semua promise yang terdaftar diselesaikan dan array dari hasil promise menjadi hasilnya itu sendiri.\n\nSebagai contoh, `Promise.all` di bawah selesai setelah 3 detik, dan kemudian hasilnya adalah sebuah array `[1, 2, 3]`:\n\n```js run\nPromise.all([\n  new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1\n  new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2\n  new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3\n]).then(alert); // 1,2,3 ketika promise sudah siap: setiap promise menyumbangkan sebuah member array\n```\n\nHarap dicatat bahwa urutan member array yang dihasilkan adalah sama dengan sumber promise. Meskipun promise pertama membutuhkan waktu yang lama untuk resolve, promise tersebut masih yang pertama di dalam hasil array.\n\nSebuah trik umum adalah untuk memetakan sebuah array dari data pekerjaan kedalam array promise, dan kemudian membungkusnya kedalam `Promise.all`.\n\nSebagai contoh, jika kita memiliki array URL, kita dapat mengambil array-array tersebut seperti ini:\n\n```js run\nlet urls = [\n  \"https://api.github.com/users/iliakan\",\n  \"https://api.github.com/users/remy\",\n  \"https://api.github.com/users/jeresig\",\n];\n\n// memetakan setiap url ke pengambilan promise\nlet requests = urls.map((url) => fetch(url));\n\n// Promise.all menunggu sampai semua pekerjaan telah diresolve\nPromise.all(requests).then((responses) =>\n  responses.forEach((response) => alert(`${response.url}: ${response.status}`))\n);\n```\n\n\nContoh terbesar dengan mengambil informasi pengguna untuk sebuah array pengguna GitHub dengan nama mereka (kita dapat mengambil array dengan id mereka, logika nya sama):\n\n```js run\nlet names = [\"iliakan\", \"remy\", \"jeresig\"];\n\nlet requests = names.map((name) =>\n  fetch(`https://api.github.com/users/${name}`)\n);\n\nPromise.all(requests)\n  .then((responses) => {\n    // semua response telah sukses diresolve\n    for (let response of responses) {\n      alert(`${response.url}: ${response.status}`); // menunjukkan 200 untuk setiap url\n    }\n\n    return responses;\n  })\n\n  // memetakan array response kedalam array response.json() untuk membaca isinya\n  .then((responses) => Promise.all(responses.map((r) => r.json())))\n  // semua jawaban JSON diuraikan: \"users\" adalah array dari jawaban tersebut\n  .then((users) => users.forEach((user) => alert(user.name)));\n```\n\n**Jika ada promise yang direject, promise tersebut dikembalikkan oleh `Promise.all` secara langsung me-reject nya dengan error itu.**\n\nSebagai contoh:\n\n```js run\nPromise.all([\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),\n*!*\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Whoops!\")), 2000)),\n*/!*\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))\n]).catch(alert); // Error: Whoops!\n```\n\n\nDisini promise kedua reject dalam dua detik. Itu langsung mengarah pada rejection `Promise.all`, jadi eksekusi `.catch`: error rejection menjadi hasil keseluruhan `Promise.all`.\n\n\n```warn header=\"Jika terjadi sebuah error, promise lain diabaikan\"\nJika satu promise reject, `Promise.all` langsung reject, benar-benar melupakan yang lainnya yang ada di dalam daftar. Hasil dari promise-promise tersebut diabaikan.\n\nSebagai contoh, jika disana ada banyak pemanggilan `fetch`, seperti contoh di atas, dan satu gagal, yang lainnya akan terus mengeksekusi, tetapi `Promise.all` tidak akan memperhatikan promise-promisenya lagi. Promise-promise tersebut mungkin selesai, tetapi hasilnya akan diabaikan.\n\n`Promise.all` tidak melakukan apapun untuk membatalkan promise-promise tersebut, karena tidak ada konsep \"pembatalan\" di dalam promise. Di [bab lainnya](info:fetch-abort) kita akan membahas `AbortController` yang bisa membantu, tetapi `AbortController` tersebut bukan bagian dari API Promise.\n```\n\n````smart header=\"`Promise.all(iterable)`memungkinkan nilai \\\"regular\\\" non-promise di dalam `iterable`\" Secara normal, `Promise.all(...)` menerima sebuah promise iterable (dalam banyak kasus sebuah array). Tetapi jika salah satu objek bukan promise, objek tersebut diteruskan ke array yang dihasilkan \"sebagaimana adanya\".\n\nSebagai contoh, berikut hasilnya `[1, 2, 3]`:\n\n```js run\nPromise.all([\n  new Promise((resolve, reject) => {\n    setTimeout(() => resolve(1), 1000);\n  }),\n  2,\n  3,\n]).then(alert); // 1, 2, 3\n```\n\nJadi kita dapat meneruskan nilai yang sudah siap ke `Promise.all` jika nyaman.\n\n````\n\n## Promise.allSettled\n\n[recent browser=\"new\"]\n\n`Promise.all` reject seluruhnya jika ada promise yang reject. Itu bagus untuk kasus \"semua atau tidak sama sekali\", ketika kita membutuhkan *semua* hasil untuk melanjutkan:\n\n```js\nPromise.all([\n  fetch('/template.html'),\n  fetch('/style.css'),\n  fetch('/data.json')\n]).then(render); // method render butuh hasil dari semua pengambilan\n````\n\n\n`Promise.allSettled` menunggu semua promise selesai. Array yang dihasilkan memiliki:\n\n- `{status:\"fulfilled\", value:result}` untuk response sukses,\n- `{status:\"rejected\", reason:error}` untuk error.\n\nSebagai contoh, kita ingin mengambil informasi tentang banyak pengguna. Bahkan jika satu request gagal, kita masih tertarik pada yang request yang lain.\n\nMari gunakan `Promise.allSettled`:\n\n```js run\nlet urls = [\n  \"https://api.github.com/users/iliakan\",\n  \"https://api.github.com/users/remy\",\n  \"https://no-such-url\",\n];\n\nPromise.allSettled(urls.map((url) => fetch(url))).then((results) => {\n  // (*)\n  results.forEach((result, num) => {\n    if (result.status == \"fulfilled\") {\n      alert(`${urls[num]}: ${result.value.status}`);\n    }\n    if (result.status == \"rejected\") {\n      alert(`${urls[num]}: ${result.reason}`);\n    }\n  });\n});\n```\n\n`results` pada baris `(*)` di atas akan:\n\n```js\n[\n  {status: 'fulfilled', value: ...response...},\n  {status: 'fulfilled', value: ...response...},\n  {status: 'rejected', reason: ...error object...}\n]\n```\n\nJadi, untuk setiap promise kita mendapatkan status-nya dan `value/error`.\n\n### Polyfill\n\nJika peramban tidak mendukung `Promise.allSettled`, mudah untuk melakukan polyfill:\n\n```js\nif (!Promise.allSettled) {\n  const rejectHandler = reason => ({ status: 'rejected', reason });\n\n  const resolveHandler = value => ({ status: 'fulfilled', value });\n\n  Promise.allSettled = function (promises) {\n    const convertedPromises = promises.map(p => Promise.resolve(p).then(resolveHandler, rejectHandler));\n    return Promise.all(convertedPromises);\n  };\n}\n```\n\nDalam kode ini, `promises.map` mengambil nilai input, berubah menjadi promises (untuk berjaga-jaga jika non-promise yang diteruskan) dengan `p => Promise.resolve(p)`, dan kemudian menambahkan handler `.then` handler ke semuanya.\n\nHandler tersebut mengubah hasil `v` yang sukses menjadi `{state:'fulfilled', value:v}`, dan sebuah error `r` menjadi `{state:'rejected', reason:r}`. Itu persis dengan format `Promise.allSettled`.\n\nKita bisa menggunakan `Promise.allSettled` untuk mendapatkan hasilnya atau _semua_ promise diberikan, bahkan jika beberapa dari promise itu reject.\n\n## Promise.race\n\nMirip dengan `Promise.all`, tetapi hanya menunggu promise pertama diselesaikan, dan mendapatkan hasilnya (atau error).\n\nSintaksisnya adalah:\n\n```js\nlet promise = Promise.race(iterable);\n```\n\nSebagai contoh, hasil di sini akan menjadi `1`:\n\n```js run\nPromise.race([\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),\n  new Promise((resolve, reject) =>\n    setTimeout(() => reject(new Error(\"Whoops!\")), 2000)\n  ),\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),\n]).then(alert); // 1\n```\n\nPromise pertama di sini adalah yang tercepat, jadi promise tersebut menjadi hasilnya. Setelah promise pertama yang selesai \"memenangkan balapan\", semua hasil/errors lebih lanjut akan diabaikan.\n\n## Promise.any\n\nSimilar to `Promise.race`, but waits only for the first fulfilled promise and gets its result. If all of the given promises are rejected, then the returned promise is rejected with [`AggregateError`](mdn:js/AggregateError) - a special error object that stores all promise errors in its `errors` property.\n\nThe syntax is:\n\n```js\nlet promise = Promise.any(iterable);\n```\n\nFor instance, here the result will be `1`:\n\n```js run\nPromise.any([\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Whoops!\")), 1000)),\n  new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),\n  new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))\n]).then(alert); // 1\n```\n\nThe first promise here was fastest, but it was rejected, so the second promise became the result. After the first fulfilled promise \"wins the race\", all further results are ignored.\n\nHere's an example when all promises fail:\n\n```js run\nPromise.any([\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Ouch!\")), 1000)),\n  new Promise((resolve, reject) => setTimeout(() => reject(new Error(\"Error!\")), 2000))\n]).catch(error => {\n  console.log(error.constructor.name); // AggregateError\n  console.log(error.errors[0]); // Error: Ouch!\n  console.log(error.errors[1]); // Error: Error\n});\n```\n\nAs you can see, error objects for failed promises are available in the `errors` property of the `AggregateError` object.\n\n## Promise.resolve/reject\n\nMethod `Promise.resolve` dan `Promise.reject` jarang dibutuhkan dalam kode modern, karena sintaksis `async/await` (kita akan membahasnya [nanti](info:async-await)) membuat method-method tersebut usang.\n\nKita membahas method-method tersebut di sini sebagai pelengkap, dan bagi mereka yang tidak bisa menggunakan `async/await` untuk beberapa alasan.\n\n- `Promise.resolve(value)` membuat promise yang diresolve dengan hasil `value`.\n\nSama dengan:\n\n```js\nlet promise = new Promise((resolve) => resolve(value));\n```\n\nMethod ini digunakan untuk kompatibilitas, ketika function diharapkan untuk mengembalikkan promise.\n\nSebagai contoh, function `loadCached` di bawah mengambil URL dan mengingat (cache) isinya. Untuk panggilan di masa mendatang dengan URL yang sama itu segera mendapatkan isi sebelumnya dari cache, tetapi menggunakan `Promise.resolve` untuk membuat promise tentang itu, jadi hasil yang dikembalikkan selalu sebuah promise:\n\n```js\nlet cache = new Map();\n\nfunction loadCached(url) {\n  if (cache.has(url)) {\n*!*\n    return Promise.resolve(cache.get(url)); // (*)\n*/!*\n  }\n\n  return fetch(url)\n    .then(response => response.text())\n    .then(text => {\n      cache.set(url,text);\n      return text;\n    });\n}\n```\n\nKita dapat menulis `loadCached(url).then(…)`, karena function tersebut dijamin untuk mengembalikan promise. Kita selalu dapat menggunakan `.then` setelah `loadCached`. Itulah tujuan dari `Promise.resolve` pada baris `(*)`.\n\n### Promise.reject\n\n- `Promise.reject(error)` membuat promise yang direjecet dengan `error`.\n\nSama dengan:\n\n```js\nlet promise = new Promise((resolve, reject) => reject(error));\n```\n\nDalam praktiknya, method ini hampir tidak pernah digunakan.\n\n## Ringkasan\n\nAda 6 method static dari class `Promise`:\n\n1. `Promise.all(promises)` -- menunggu semua promise selesai dan mengambalikan sebuah array sebagai hasilnya. Jika salah satu promises yang diberikan reject, maka menjadi error of `Promise.all`, dan semua hasil lainnya akan diabaikan.\n2. `Promise.allSettled(promises)` (method yang baru ditambahkan) -- menunggu semua promise selesai dan mengembalikan hasilnya sebagai objek array dengan:\n   - `state`: `\"fulfilled\"` or `\"rejected\"`\n   - `value` (jika fulfilled) atau `reason` (jika rejected).\n3. `Promise.race(promises)` -- menunggu promise pertama selesai, dan hasil/error menjadi hasilnya.\n4. `Promise.any(promises)` (metode baru yang ditambahkan) - menunggu promise pertama terpenuhi, dan hasilnya menjadi hasil keseluruhan. Jika semua janji yang diberikan ditolak, [`AggregateError`] (mdn: js / AggregateError) menjadi eror`Promise.any`.\n5. `Promise.resolve(value)` -- membuat promise yang resolved dengan nilai yang diberikan.\n6. `Promise.reject(error)` -- membuat promise rejected dengan error yang diberikan.\n\n\nDari semua ini, `Promise.all` mungkin yang paling umum dalam praktiknya.\n\n```\n\n```\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/head.html",
    "content": "<script>\nfunction loadScript(src) {\n  return new Promise(function(resolve, reject) {\n    let script = document.createElement('script');\n    script.src = src;\n\n    script.onload = () => resolve(script);\n    script.onerror = () => reject(new Error(\"Script load error: \" + src));\n\n    document.head.append(script);\n  });\n}\n</script>\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/iliakan.json",
    "content": "{\n  \"name\": \"iliakan\",\n  \"isAdmin\": true\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/one.js",
    "content": "function one() {\n  alert(1);\n}\n"
  },
  {
    "path": "1-js/11-async/05-promise-api/two.js",
    "content": "function two() {\n  alert(2);\n}\n"
  },
  {
    "path": "1-js/11-async/06-promisify/article.md",
    "content": "# Promisifikasi\n\nPromisifikasi -- adalah kata yang panjang untuk transformasi sederhana. Ini adalah konversi dari function yang menerima sebuah callback menjadi function yang mengembalikkan sebuah promise.\n\nTransformasi seperti itu sering kali dibutuhkan dalam kehidupan nyata sebanyak function dan pustaka berbasis callback. Tetapi promise lebih nyaman. Jadi masuk akal untuk melakukan promisify ini.\n\nUntuk pemahaman yang lebih baik, mari kita lihat contoh.\n\nMisalnya, kita memiliki `loadScript (src, callback)` dari bab <info: callbacks>.\n\n```js run\nfunction loadScript(src, callback) {\n  let script = document.createElement(\"script\");\n  script.src = src;\n\n  script.onload = () => callback(null, script);\n  script.onerror = () => callback(new Error(`Script load error for ${src}`));\n\n  document.head.append(script);\n}\n\n// penggunaan:\n// loadScript('path/script.js', (err, script) => {...})\n```\n\nFungsi memuat script dengan `src` yang diberikan, dan kemudian memanggil` callback (err) `jika terjadi kesalahan, atau` callback (null, script) `jika pemuatan berhasil. Itu adalah kesepakatan untuk menggunakan callback, kita telah melihat itu sebelumnya.\n\nMari kita jadikan promise.\n\nKita akan membuat fungsi baru `loadScriptPromise (src)`, yang melakukan hal yang sama (memuat skrip), tetapi mengembalikan sebuah promise daripada menggunakan callback.\n\nDengan kata lain, kita hanya meneruskan `src` (tanpa` callback`) dan mendapatkan janji sebagai gantinya, yang menyelesaikan dengan `script` ketika pemuatan berhasil, dan menolak dengan kesalahan sebaliknya.\n\nHere it is:\n```js\nlet loadScriptPromise = function (src) {\n  return new Promise((resolve, reject) => {\n    loadScript(src, (err, script) => {\n      if (err) reject(err);\n      else resolve(script);\n    });\n  });\n};\n\n// penggunaan:\n// loadScriptPromise('path/script.js').then(...)\n```\n\nSeperti yang bisa kita lihat, fungsi baru adalah pembungkus di sekitar fungsi `loadScript` asli. Ia menyebutnya menyediakan callback sendiri yang diterjemahkan menjadi janji `menyelesaikan / menolak`.\n\nSekarang `loadScriptPromise` cocok dengan kode berbasis promise. Jika kita lebih menyukai promise daripada callback (dan segera kita akan melihat lebih banyak alasan untuk itu), maka kita akan menggunakannya.\n\nDalam praktiknya kita mungkin perlu menjanjikan lebih dari satu fungsi, jadi masuk akal untuk menggunakan helper.\n\nKita akan menyebutnya `promisify (f)`: ia menerima fungsi to-promisify `f` dan mengembalikan fungsi pembungkus.\n\n```js\nfunction promisify(f) {\n  return function (...args) { // return a wrapper-function (*)\n    return new Promise((resolve, reject) => {\n      function callback(err, result) { // our custom callback for f (**)\n        if (err) {\n          reject(err);\n        } else {\n          resolve(result);\n        }\n      }\n\n      args.push(callback); // tambahkan callback khusus kita ke akhir argumen f\n\n      f.call(this, ...args); // panggil fungsi aslinya\n    });\n  };\n}\n\n// penggunaan:\nlet loadScriptPromise = promisify(loadScript);\nloadScriptPromise(...).then(...);\n```\n\n\nKode mungkin terlihat agak rumit, tetapi pada dasarnya sama dengan yang kita tulis di atas, sambil menjanjikan fungsi `loadScript`.\n\nPanggilan ke `promisify (f)` mengembalikan pembungkus di sekitar `f`` (*) `. Wrapper tersebut mengembalikan sebuah promise dan meneruskan panggilan ke `f` asli, melacak hasilnya di callback kustom` (**) `.\n\nDi sini, `promisify` mengasumsikan bahwa fungsi asli mengharapkan callback dengan tepat dua argumen` (err, result) `. Itulah yang paling sering kita temui. Maka callback khusus kita berada dalam format yang benar-benar tepat, dan `promisify` berfungsi dengan baik untuk kasus seperti itu.\n\n\nTetapi bagaimana jika `f` asli mengharapkan callback dengan lebih banyak argumen` callback (err, res1, res2, ...) `?\n\nKami dapat meningkatkan penolong kami. Mari buat versi lanjutan dari `promisify`.\n\n- Ketika dipanggil sebagai `promisify (f)` seharusnya berfungsi seperti versi di atas.\n- Ketika dipanggil sebagai `promisify (f, true)`, itu harus mengembalikan promise yang diselesaikan dengan array hasil callback. Itu persis untuk callback dengan banyak argumen.\n\n```js\n// promisify(f, true) dapatkan array dari hasil\nfunction promisify(f, manyArgs = false) {\n  return function (...args) {\n    return new Promise((resolve, reject) => {\n      function *!*callback(err, ...results*/!*) { // callback khusus kita untuk f\n        if (err) {\n          reject(err);\n        } else {\n          // resolve dengan semua hasil callback jika manyArgs ditentukan\n          *!*resolve(manyArgs ? results : results[0]);*/!*\n        }\n      }\n\n      args.push(callback);\n\n      f.call(this, ...args);\n    });\n  };\n}\n\n// penggunaan:\nf = promisify(f, true);\nf(...).then(arrayOfResults => ..., err => ...);\n```\n\nSeperti yang Anda lihat, ini pada dasarnya sama seperti di atas, tetapi `menyelesaikan` dipanggil dengan hanya satu atau semua argumen bergantung pada apakah` manyArgs` benar.\n\nUntuk format callback yang lebih eksotis, seperti yang tidak memiliki `err` sama sekali:` callback (result) `, kita bisa memastikan fungsi tersebut secara manual tanpa menggunakan helper.\n\nAda juga module dengan function promisifikasi yang sedikit lebih fleksibel, misalnya [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). Di Node.js, ada function `util.promisify` bawaan untuk itu.\n\n```smart\nPromisifikasi adalah pendekatan yang bagus, khususnya ketika anda menggunakan `async/await` (lihat bab selanjutnya), tapi bukan pengganti callback secara total.\n\nIngat, sebuah promise mungkin hanya memiliki satu hasil, tetapi callback mungkin secara teknis dipanggil berkali-kali.\n\nJadi promisifikasi hanya dimaksudkan untuk function yang memanggil callback sekali. Panggilan selanjutnya akan diabaikan.\n```\n"
  },
  {
    "path": "1-js/11-async/07-microtask-queue/article.md",
    "content": "# Microtasks\n\nHandler-handler promise `.then`/`.catch`/`.finally` selalu asynchronous.\n\nBahkan ketika sebuah promise diresolve, kode pada baris _di bawah_ `.then`/`.catch`/`.finally` masih akan dieksekusi sebelum handler-handler ini.\n\nBerikut demo-nya:\n\n```js run\nlet promise = Promise.resolve();\n\npromise.then(() => alert(\"promise done!\"));\n\nalert(\"code finished\"); // alert ini muncul lebih dahulu\n```\n\nJika anda menjalankannya, anda melihat `code finished` lebih dahulu, dan kemudian `promise done!`.\n\nItu aneh, karena promise pasti _done_ dari awal.\n\nMengapa `.then` terpicu setelahnya? Apa yang sedang terjadi?\n\n## Antrean Microtasks\n\n\nTask asynchronous membutuhkan manajemen yang tepat. Untuk itu, standar menentukan antrean internal `PromiseJobs`, lebih sering disebut sebagai \"antrean microtask\" (istilah v8).\n\nSeperti yang dikatakan di [spesifikasi](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues):\n\n- Antrean adalah yang pertama masuk-pertama keluar: tasks yang diantrekan pertama dijalankan terlebih dahulu.\n- Eksekusi dari task dimulai jika hanya tidak ada yang berjalan.\n\n\nAtau, untuk mengatakannya secara sederhana, ketika promise sudah siap, handler-handler `.then/catch/finally` ini dimasukkan kedalam antrean. Handler-handler tersebut belum dieksekusi. Mesin JavaScript mengambil tugas dari antrean dan menjalankannya.\n\nItulah kenapa \"code finished\" pada contoh di atas muncul lebih dahulu.\n\n![](promiseQueue.svg)\n\n\nHandler-handler promise selalu melalui antrean internal.\n\nJika ada chain dengan banyak `.then/catch/finally`, maka masing-masing dieksekusi secara asynchronous. Artinya, itu pertama kali masuk ke antrean, dan dieksekusi ketika kode saat ini sudah selesai dan antrean handler-handler sebelumnya sudah selesai.\n\n**Bagaimana jika urutan penting untuk kita? Bagaimana kita membuat `code finished` berjalan setelah `promise done`?**\n\n\nMudah, letakkan saja di dalam antrean dengan `.then`:\n\n```js run\nPromise.resolve()\n  .then(() => alert(\"promise done!\"))\n  .then(() => alert(\"code finished\"));\n```\n\nSekarang urutannya seperti yang diinginkan.\n\n## Rejection yang tidak tertangani\n\nIngat event `unhandledrejection` dari bab <info:promise-error-handling>?\n\nSekarang kita bisa melihat bagaimana sebenarnya JavaScript menemukan bahwa ada rejection yang tidak tertangani.\n\n**\"Rejection yang tidak tertangani\" muncul ketika error promise tidak ditangani di akhir antrean microtask.**\n\nBiasanya, jika kita mengharapkan error, kita menambahkan `.catch` ke chain promise untuk menangani error tersebut:\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n*!*\npromise.catch(err => alert('caught'));\n*/!*\n\n// tidak berjalan: error ditangani\nwindow.addEventListener('unhandledrejection', event => alert(event.reason));\n```\n\n\n...Tetapi jika kita lupa menambah `.catch`, kemudian, setelah antrean microtask sudah kosong, mesin memicu event:\n\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n\n// Promise Gagal!\nwindow.addEventListener(\"unhandledrejection\", (event) => alert(event.reason));\n```\n\nBagaimana jika kita menangani error tersebut nanti? Seperti ini:\n\n```js run\nlet promise = Promise.reject(new Error(\"Promise Failed!\"));\n*!*\nsetTimeout(() => promise.catch(err => alert('caught')), 1000);\n*/!*\n\n// Error: Promise Gagal!\nwindow.addEventListener('unhandledrejection', event => alert(event.reason));\n```\n\n\nSekarang, jika anda menjalankannya, kita akan melihat pesan `Promise Failed!` terlebih dahulu, dan kemudian `caught`.\n\nJika kita tidak tahu tentang antrean microtasks, kita bisa bertanya-tanya: \"Mengapa handler `unhandledrejection` berjalan? Kita menangkap error!\".\n\nTetapi sekarang kita mengerti bahwa `unhandledrejection` dihasilkan saat antrean microtask selesai: mesin memeriksa promise dan, jika ada promise yang berada di state \"rejected\", maka event akan dipicu.\n\nPada contoh di atas, `.catch` ditambahkan oleh `setTimeout` juga pemicu, tetapi kemudian, setelah `unhandledrejection` telah terjadi, jadi itu tidak mengubah apapun.\n\n\n## Ringkasan\n\nPenanganan promise selalu asynchronous, karena semua aksi promise melewati antrean internal \"promise jobs\", juga dipanggil \"antrean microtask\" (istilah v8).\n\nJadi, handler-handler `.then/catch/finally` selalu dipanggil setelah kode saat ini selesai.\n\nJika kita butuh untuk menjamin kalau potongan kode dieksekusi setelah `.then/catch/finally`, kita bisa menambahnya kedalam panggilan chain `.then`.\n\n\nDi sebagian besar mesin Javascript, termasuk peramban dan Node.js, konsep microtasks terkait erat dengan \"event loop\" dan \"macrotasks\". Karena ini tidak berhubungan langsung dengan promise, konsep-konsep tersebut akan dibahas di bagian lain pada tutorial, dalam bab <info:event-loop>.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/01-rewrite-async/solution.md",
    "content": "Catatannya ada di bawah kode:\n\n```js run\nasync function loadJson(url) {\n  // (1)\n  let response = await fetch(url); // (2)\n\n  if (response.status == 200) {\n    let json = await response.json(); // (3)\n    return json;\n  }\n\n  throw new Error(response.status);\n}\n\nloadJson(\"no-such-user.json\").catch(alert); // Error: 404 (4)\n```\n\nCatatan:\n\n1. Function `loadJson` menjadi `async`.\n2. Semua `.then` di dalamnya di ganti dengan `await`.\n3. Kita dapat `return response.json()` daripada menunggu untuk itu, seperti ini:\n\n   ```js\n   if (response.status == 200) {\n     return response.json(); // (3)\n   }\n   ```\n\n   Lalu kode terluar harus `await` untuk promise tersebut resolve. Dalam kasus kita, itu tidak masalah.\n\n4. Error yang dilempar dari `loadJson` ditangani oleh `.catch`. Kita tidak bisa menggunakan `await loadJson(…)` di sana, karena kita tidak berada di dalam function `async`.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/01-rewrite-async/task.md",
    "content": "# Menulis ulang menggunakan async/await\n\nTulis ulang salah satu contoh di bab ini <info:promise-chaining> menggunakan `async/await` daripada `.then/catch`:\n\n```js run\nfunction loadJson(url) {\n  return fetch(url)\n    .then(response => {\n      if (response.status == 200) {\n        return response.json();\n      } else {\n        throw new Error(response.status);\n      }\n    });\n}\n\nloadJson('no-such-user.json')\n  .catch(alert); // Error: 404\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/02-rewrite-async-2/solution.md",
    "content": "Tidak ada trik di sini. Hanya mengganti `.catch` dengan `try...catch` di dalam `demoGithubUser` dan menambahkan `async/await` ketika dibutuhkan:\n\n```js run\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = \"HttpError\";\n    this.response = response;\n  }\n}\n\nasync function loadJson(url) {\n  let response = await fetch(url);\n  if (response.status == 200) {\n    return response.json();\n  } else {\n    throw new HttpError(response);\n  }\n}\n\n// Tanya nama pengguna sampai github mengembalikkan pengguna yang valid\nasync function demoGithubUser() {\n  let user;\n  while (true) {\n    let name = prompt(\"Enter a name?\", \"iliakan\");\n\n    try {\n      user = await loadJson(`https://api.github.com/users/${name}`);\n      break; // tidak ada error, keluar dari loop\n    } catch (err) {\n      if (err instanceof HttpError && err.response.status == 404) {\n        // loop dilanjutkan setelah alert\n        alert(\"No such user, please reenter.\");\n      } else {\n        // error yang tidak diketahui, rethrow\n        throw err;\n      }\n    }\n  }\n\n  alert(`Full name: ${user.name}.`);\n  return user;\n}\n\ndemoGithubUser();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/02-rewrite-async-2/task.md",
    "content": "# Menulis ulang \"rethrow\" dengan async/await\n\nDi bawah anda dapat menemukan contoh \"rethrow\" dari bab <info:promise-chaining>. Tulis ulang menggunakan `async/await` daripada `.then/catch`.\n\nDan singkirkan rekursi yang mendukung masuk loop dalam `demoGithubUser`: dengan `async/await` itu menjadi mudah untuk dilakukan.\n\n```js run\nclass HttpError extends Error {\n  constructor(response) {\n    super(`${response.status} for ${response.url}`);\n    this.name = \"HttpError\";\n    this.response = response;\n  }\n}\n\nfunction loadJson(url) {\n  return fetch(url).then((response) => {\n    if (response.status == 200) {\n      return response.json();\n    } else {\n      throw new HttpError(response);\n    }\n  });\n}\n\n// Tanya nama pengguna sampai github mengembalikkan user yang valid\nfunction demoGithubUser() {\n  let name = prompt(\"Enter a name?\", \"iliakan\");\n\n  return loadJson(`https://api.github.com/users/${name}`)\n    .then((user) => {\n      alert(`Full name: ${user.name}.`);\n      return user;\n    })\n    .catch((err) => {\n      if (err instanceof HttpError && err.response.status == 404) {\n        alert(\"No such user, please reenter.\");\n        return demoGithubUser();\n      } else {\n        throw err;\n      }\n    });\n}\n\ndemoGithubUser();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/03-async-from-regular/solution.md",
    "content": "Itulah yang terjadi ketika mengetahui cara kerjanya di dalam sangat membantu.\n\nHanya perlakukan pemanggilan `async` sebagai promise dan lampirkan `.then` ke dalamnya:\n\n```js run\nasync function wait() {\n  await new Promise(resolve => setTimeout(resolve, 1000));\n\n  return 10;\n}\n\nfunction f() {\n  // menunjukkan 10 setelah 1 detik\n*!*\n  wait().then(result => alert(result));\n*/!*\n}\n\nf();\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/03-async-from-regular/task.md",
    "content": "# Panggil async dari non-async\n\nKita punya function \"reguler\". Bagaimana memanggil `async`dari function tersebut dan menggunakan hasilnya?\n\n```js\nasync function wait() {\n  await new Promise((resolve) => setTimeout(resolve, 1000));\n\n  return 10;\n}\n\nfunction f() {\n  // ...apa  yang ditulis di sini?\n  // kita harus memanggil async wait() dan tunggu sampai mendapatkan 10\n  // ingat, kita tidak bisa menggunakan \"await\"\n}\n```\n\nP.S. Task ini secara teknis sangat mudah, tetapi pertanyaan ini cukup umum bagi developer yang baru mengenal async/await.\n"
  },
  {
    "path": "1-js/11-async/08-async-await/article.md",
    "content": "# Async/await\n\nAda sintaksis spesial untuk bekerja dengan promise dengan cara yang lebih nyaman, dipanggil \"async/await\". Ini sangat mudah dipahami dan digunakan.\n\n## Fungsi Async\n\nMari mulai dengan keyword `async`. keyword ini dapat ditempatkan sebelum fungsi, seperti ini:\n\n```js\nasync function f() {\n  return 1;\n}\n```\n\nKata \"async\" sebelum fungsi berarti satu hal sederhana: fungsi tersebut selalu mengembalikkan promise. Value lain dibungkus didalam promise yang resolve secara otomatis.\n\nSebagai contoh, fungsi ini mengembalikkan promise yang resolve dengan hasil `1`, mari kita uji:\n\n```js run\nasync function f() {\n  return 1;\n}\n\nf().then(alert); // 1\n```\n\n...Kita secara eksplisit dapat mengembalikkan promise, itu akan sama dengan:\n\n```js run\nasync function f() {\n  return Promise.resolve(1);\n}\n\nf().then(alert); // 1\n```\n\nJadi, `async` memastikan bahwa fungsi mengembalikkan promise, dan membungkus non-promise di dalamnya. Cukup mudah, bukan? Tapi tidak hanya itu. Ada keyword lain, `await`, yang hanya bekerja di dalam fungsi `async`, dan itu cukup keren.\n\n## Await\n\nSintaksis:\n\n```js\n// bekerja hanya di dalam fungsi async\nlet value = await promise;\n```\n\nKeyword `await` membuat JavaScript menunggu sampai promise tersebut selesai dan mengembalikkan hasilnya.\n\nIni contoh dengan promise yang selesai dalam 1 detik:\n\n```js run\nasync function f() {\n\n  let promise = new Promise((resolve, reject) => {\n    setTimeout(() => resolve(\"done!\"), 1000)\n  });\n\n*!*\n  let result = await promise; // tunggu sampai promise selesai (*)\n*/!*\n\n  alert(result); // \"done!\"\n}\n\nf();\n```\n\nEksekusi fungsi tersebut \"dipause\" pada baris `(*)` dan dilanjutkan ketika promise selesai, dengan `result` menjadi hasilnya. Jadi kode di atas menunjukkan \"done!\" dalam satu detik.\n\nMari kita tekankan: `await` benar-benar membuat JavaScript menunggu sampai promise selesai, lalu lanjutkan dengan hasilnya. Hal tersebut tidak membebani _resource_ CPU apapun, karena mesin dapat melakukan pekerjaan lain sementara itu: eksekusi script lain, menangani event dan lain-lain.\n\nIni hanya sintaksis yang lebih elegan untuk mendapatkan hasil dari promise daripada `promise.then`, mudah untuk dibaca dan ditulis.\n\n````warn header=\"Tidak dapat menggunakan `await`di dalam fungsi biasa\" Jika kita coba untuk menggunakan`await` di dalam fungsi non-async, akan ada error sintaksis:\n\n```js run\nfunction f() {\n  let promise = Promise.resolve(1);\n*!*\n  let result = await promise; // Error sintaksis\n*/!*\n}\n```\n\nKita akan mendapatkan error ini jika kita tidak meletakkan `async` sebelum fungsi. Seperti yang dikatakan, `await` hanya bekerja di dalam sebuah `fungsi async`.\n\n`````\nMari kita ambil contoh `showAvatar()` dari bab <info:promise-chaining> dan menulisnya ulang menggunakan `async/await`:\n\n1. Kita harus mengganti call `.then` dengan `await`.\n2. Juga kita harus membuat fungsi `async` agar mereka bekerja.\n\n```js run\nasync function showAvatar() {\n\n  // baca JSON kita\n  let response = await fetch('/article/promise-chaining/user.json');\n  let user = await response.json();\n\n  // baca pengguna github\n  let githubResponse = await fetch(`https://api.github.com/users/${user.name}`);\n  let githubUser = await githubResponse.json();\n\n  // memunculkan avatar\n  let img = document.createElement('img');\n  img.src = githubUser.avatar_url;\n  img.className = \"promise-avatar-example\";\n  document.body.append(img);\n\n  // tunnggu 3 detik\n  await new Promise((resolve, reject) => setTimeout(resolve, 3000));\n\n  img.remove();\n\n  return githubUser;\n}\n\nshowAvatar();\n```\n\nCukup bersih dan mudah dibaca, bukan? Jauh lebih baik dari sebelumnya.\n\n````smart header=\"`await` tidak bekerja pada top-level code\"\nOrang yang baru mulai menggunakan `await` cenderung lupa faktanya bahwa kita tidak bisa menggunakan `await` pada top-level code. Sebagai contoh, ini tidak akan bekerja:\n\n```js run\n// error sintaksis pada top-level code\nlet response = await fetch('/article/promise-chaining/user.json');\nlet user = await response.json();\n```\n\nKita dapat membungkusnya kedalam fungsi async anonymous, seperti ini:\n\n```js\n(async () => {\n  let response = await fetch('/article/promise-chaining/user.json');\n  let user = await response.json();\n  ...\n})();\n```\n\nP.S. Fitur baru: mulai dari mesin V8 versi 8.9+, tingkat atas menunggu bekerja di [modul](info: modul).\n````\n\n`````\n\n````smart header=\"`await`menerima \\\"thenables\\\"\" Seperti`promise.then`, `await`memperbolehkan kita menggunakan objek thenable (mereka dengan method`then`callable). Idenya adalah objek 3rd-party mungkin bukan promise, tetapi promise-compatible: jika objek tersebut mendukung`.then`, itu cukup digunakan dengan `await`.\n\nDisini demo class `Thenable`, `await` di bawah menerima instances dari class:\n\n```js run\nclass Thenable {\n  constructor(num) {\n    this.num = num;\n  }\n  then(resolve, reject) {\n    alert(resolve);\n    // resolve dengan this.num*2 setelah 1000ms\n    setTimeout(() => resolve(this.num * 2), 1000); // (*)\n  }\n}\n\nasync function f() {\n  // tunggu selama 1 detik, kemudian result-nya menjadi 2\n  let result = await new Thenable(1);\n  alert(result);\n}\n\nf();\n```\n\nJika `await` mendapatkan objek non-promise dengan `.then`, objek itu memanggil method yang menyediakan fungsi asli `resolve`, `reject` sebagai argumen. Kemudian `await` menunggu sampai salah satu argumen tersebut dipanggil (pada contoh di atas hal tersebut terjadi pada baris `(*)`) dan kemudian melanjutkan dengan hasilnya.\n\n`````\n\n````smart header=\"Method class async \"\nUntuk mendeklarasikan sebuah method class async, cukup tambahkan saja `async`:\n\n```js run\nclass Waiter {\n*!*\n  async wait() {\n*/!*\n    return await Promise.resolve(1);\n  }\n}\n\nnew Waiter()\n  .wait()\n  .then(alert); // 1 (this is the same as (result => alert(result)))\n```\nArtinya sama saja: itu memastikan bahwa value yang dikembalikkan adalah promise dan memungkinkan `await`.\n\n`````\n\n## Penanganan error\n\nJika sebuah promise resolve secara normal, kemudian `await promise` mengembalikkan result. Tetapi dalam kasus rejection, `await promise` melempar error, seolah olah ada pernyataan `throw` pada baris tersebut.\n\nKode ini:\n\n```js\nasync function f() {\n*!*\n  await Promise.reject(new Error(\"Whoops!\"));\n*/!*\n}\n```\n\n...Sama dengan ini:\n\n```js\nasync function f() {\n*!*\n  throw new Error(\"Whoops!\");\n*/!*\n}\n```\n\nDi dalam situasi yang nyata, promise mungkin butuh waktu sebelum reject. Dalam hal ini akan terjadi penundaan sebelum `await` melemparkan sebuah error.\n\nKita dapat catch error itu menggunakan `try..catch`, dengan cara yang sama seperti `throw` biasa:\n\n```js run\nasync function f() {\n\n  try {\n    let response = await fetch('http://no-such-url');\n  } catch(err) {\n*!*\n    alert(err); // TypeError: failed to fetch (gagal mengambil resource)\n*/!*\n  }\n}\n\nf();\n```\n\nDalam kasus error, kontrolnya meloncat ke blok `catch`. kita juga dapat membungkus banyak baris:\n\n```js run\nasync function f() {\n  try {\n    let response = await fetch(\"/no-user-here\");\n    let user = await response.json();\n  } catch (err) {\n    // catch error baik di fetch dan response.json\n    alert(err);\n  }\n}\n\nf();\n```\n\nJika kita tidak punya `try..catch`, maka promise yang dihasilkan oleh pemanggilan fungsi async `f()` menjadi direject. Kita dapat menambahkan `.catch` untuk menanganinya:\n\n```js run\nasync function f() {\n  let response = await fetch('http://no-such-url');\n}\n\n// f() menjadi sebuah promise yang direject\n*!*\nf().catch(alert); // TypeError: failed to fetch (gagal mengambil resource) // (*)\n*/!*\n```\n\nJika kita lupa menambahkan `.catch` di sana, maka kita mendapatkan sebuah error promise yang tak tertangani (dapat dilihat di console). Kita dapat catch errors seperti itu menggunakan handler event global seperti yang dijelaskan di bab <info:promise-error-handling>.\n\n```smart header=\"`async/await`dan`promise.then/catch`\" Ketika kita menggunakan `async/await`, kita jarang membutuhkan `.then`, karena `await`menangani waiting tersebut untuk kita. Dan kita dapat menggunakan sebuah `try..catch` biasa dibandingkan`.catch`. Itu biasanya (tidak selalu) lebih nyaman.\n\nTetapi pada *top-level code*, saat kita berada di luar fungsi `async`, kita secara sintaks tidak dapat menggunakan `await`, jadi itu sebuah latihan normal untuk menambah `.then/catch` untuk menangani hasil akhir atau jika terjadi error.\n\nSeperti baris `(*)` contoh di atas.\n\n`````\n\n````smart header=\"`async/await` bekerja baik dengan `Promise.all`\"\nKetika kita perlu menunggu banyak promise, kita dapat membungkusnya di dalam `Promise.all` dan kemudian `await`:\n\n```js\n// tunggu untuk result dalam array\nlet results = await Promise.all([\n  fetch(url1),\n  fetch(url2),\n  ...\n]);\n`````\n\nPada kasus error, itu menyebar seperti biasa: dari promise yang gagal ke `Promise.all`, dan kemudian menjadi sebuah pengecualian yang bisa kita catch menggunakan `try..catch` di sekitar pemanggilan.\n\n```\n\n## Ringkasan\n\nKeyword `async` sebelum fungsi memiliki dua efek:\n\n1. Membuatnya selalu mengembalikkan sebuah promise.\n2. Memperbolehkan kita untuk menggunakan `await` di dalamnya.\n\nKeyword `await` sebelum promise membuat JavaScript menunggu sampai promise itu selesai, dan kemudian:\n\n1. Jika ada error, pengecualian dihasilkan, sama seperti jika `throw error` dipanggil di tempat itu.\n2. Sebaliknya, menghasilkan result.\n\nBersama mereka menyediakan kerangka kerja yang bagus untuk menulis kode asynchronous yang mudah baik membaca dan menulis.\n\nDengan `async/await` kita jarang menulis `promise.then/catch`, tetapi kita tetap tidak boleh lupa bahwa `async/await` berdasarkan promise, karena terkadang (misalnya di scope terluar) kita harus menggunakan method ini. Juga `Promise.all` adalah sesuatu yang bagus untuk menunggu banyak task secara bersamaan.\n```\n"
  },
  {
    "path": "1-js/11-async/08-async-await/head.html",
    "content": "<style>\n.promise-avatar-example {\n  border-radius: 50%;\n  position: fixed;\n  left: 10px;\n  top: 10px;\n}\n</style>\n"
  },
  {
    "path": "1-js/11-async/index.md",
    "content": "\n# Promises, async/await\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/solution.js",
    "content": "function* pseudoRandom(seed) {\n  let value = seed;\n\n  while(true) {\n    value = value * 16807 % 2147483647\n    yield value;\n  }\n\n};\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/_js.view/test.js",
    "content": "describe(\"pseudoRandom\", function() {\n\n  it(\"follows the formula\", function() {\n    let generator = pseudoRandom(1);\n\n    assert.equal(generator.next().value, 16807);\n    assert.equal(generator.next().value, 282475249);\n    assert.equal(generator.next().value, 1622650073);\n  });\n\n\n  it(\"returns same value for the same seed\", function() {\n    let generator1 = pseudoRandom(123);\n    let generator2 = pseudoRandom(123);\n\n    assert.deepEqual(generator1.next(), generator2.next());\n    assert.deepEqual(generator1.next(), generator2.next());\n    assert.deepEqual(generator1.next(), generator2.next());\n  });\n\n});\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/solution.md",
    "content": "```js run demo\nfunction* pseudoRandom(seed) {\n  let value = seed;\n\n  while(true) {\n    value = value * 16807 % 2147483647\n    yield value;\n  }\n\n};\n\nlet generator = pseudoRandom(1);\n\nalert(generator.next().value); // 16807\nalert(generator.next().value); // 282475249\nalert(generator.next().value); // 1622650073\n```\n\nPlease note, the same can be done with a regular function, like this:\n\n```js run\nfunction pseudoRandom(seed) {\n  let value = seed;\n\n  return function() {\n    value = value * 16807 % 2147483647;\n    return value;\n  }\n}\n\nlet generator = pseudoRandom(1);\n\nalert(generator()); // 16807\nalert(generator()); // 282475249\nalert(generator()); // 1622650073\n```\n\nThat also works. But then we lose ability to iterate with `for..of` and to use generator composition, that may be useful elsewhere.\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/01-pseudo-random-generator/task.md",
    "content": "\n# Pseudo-random generator\n\nThere are many areas where we need random data.\n\nOne of them is testing. We may need random data: text, numbers, etc. to test things out well.\n\nIn JavaScript, we could use `Math.random()`. But if something goes wrong, we'd like to be able to repeat the test, using exactly the same data.\n\nFor that, so called \"seeded pseudo-random generators\" are used. They take a \"seed\", the first value, and then generate the next ones using a formula so that the same seed yields the same sequence, and hence the whole flow is easily reproducible. We only need to remember the seed to repeat it.\n\nAn example of such formula, that generates somewhat uniformly distributed values:\n\n```\nnext = previous * 16807 % 2147483647\n```\n\nIf we use `1` as the seed, the values will be:\n1. `16807`\n2. `282475249`\n3. `1622650073`\n4. ...and so on...\n\nThe task is to create a generator function `pseudoRandom(seed)` that takes `seed` and creates the generator with this formula.\n\nUsage example:\n\n```js\nlet generator = pseudoRandom(1);\n\nalert(generator.next().value); // 16807\nalert(generator.next().value); // 282475249\nalert(generator.next().value); // 1622650073\n```\n"
  },
  {
    "path": "1-js/12-generators-iterators/1-generators/article.md",
    "content": "# Generators\n\nRegular functions return only one, single value (or nothing).\n\nGenerators can return (\"yield\") multiple values, one after another, on-demand. They work great with [iterables](info:iterable), allowing to create data streams with ease.\n\n## Generator functions\n\nTo create a generator, we need a special syntax construct: `function*`, so-called \"generator function\".\n\nIt looks like this:\n\n```js\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n```\n\nGenerator functions behave differently from regular ones. When such function is called, it doesn't run its code. Instead it returns a special object, called \"generator object\", to manage the execution.\n\nHere, take a look:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\n// \"generator function\" creates \"generator object\"\nlet generator = generateSequence();\n*!*\nalert(generator); // [object Generator]\n*/!*\n```\n\nThe function code execution hasn't started yet:\n\n![](generateSequence-1.svg)\n\nThe main method of a generator is `next()`. When called, it runs the execution until the nearest `yield <value>` statement (`value` can be omitted, then it's `undefined`). Then the function execution pauses, and the yielded `value` is returned to the outer code.\n\nThe result of `next()` is always an object with two properties:\n- `value`: the yielded value.\n- `done`: `true` if the function code has finished, otherwise `false`.\n\nFor instance, here we create the generator and get its first yielded value:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\nlet generator = generateSequence();\n\n*!*\nlet one = generator.next();\n*/!*\n\nalert(JSON.stringify(one)); // {value: 1, done: false}\n```\n\nAs of now, we got the first value only, and the function execution is on the second line:\n\n![](generateSequence-2.svg)\n\nLet's call `generator.next()` again. It resumes the code execution and returns the next `yield`:\n\n```js\nlet two = generator.next();\n\nalert(JSON.stringify(two)); // {value: 2, done: false}\n```\n\n![](generateSequence-3.svg)\n\nAnd, if we call it a third time, the execution reaches the `return` statement that finishes the function:\n\n```js\nlet three = generator.next();\n\nalert(JSON.stringify(three)); // {value: 3, *!*done: true*/!*}\n```\n\n![](generateSequence-4.svg)\n\nNow the generator is done. We should see it from `done:true` and process `value:3` as the final result.\n\nNew calls to `generator.next()` don't make sense any more. If we do them, they return the same object: `{done: true}`.\n\n```smart header=\"`function* f(…)` or `function *f(…)`?\"\nBoth syntaxes are correct.\n\nBut usually the first syntax is preferred, as the star `*` denotes that it's a generator function, it describes the kind, not the name, so it should stick with the `function` keyword.\n```\n\n## Generators are iterable\n\nAs you probably already guessed looking at the `next()` method, generators are [iterable](info:iterable).\n\nWe can loop over their values using `for..of`:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  return 3;\n}\n\nlet generator = generateSequence();\n\nfor(let value of generator) {\n  alert(value); // 1, then 2\n}\n```\n\nLooks a lot nicer than calling `.next().value`, right?\n\n...But please note: the example above shows `1`, then `2`, and that's all. It doesn't show `3`!\n\nIt's because `for..of` iteration ignores the last `value`, when `done: true`. So, if we want all results to be shown by `for..of`, we must return them with `yield`:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n*!*\n  yield 3;\n*/!*\n}\n\nlet generator = generateSequence();\n\nfor(let value of generator) {\n  alert(value); // 1, then 2, then 3\n}\n```\n\nAs generators are iterable, we can call all related functionality, e.g. the spread syntax `...`:\n\n```js run\nfunction* generateSequence() {\n  yield 1;\n  yield 2;\n  yield 3;\n}\n\nlet sequence = [0, ...generateSequence()];\n\nalert(sequence); // 0, 1, 2, 3\n```\n\nIn the code above, `...generateSequence()` turns the iterable generator object into an array of items (read more about the spread syntax in the chapter [](info:rest-parameters-spread#spread-syntax))\n\n## Using generators for iterables\n\nSome time ago, in the chapter [](info:iterable) we created an iterable `range` object that returns values `from..to`.\n\nHere, let's remember the code:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  // for..of range calls this method once in the very beginning\n  [Symbol.iterator]() {\n    // ...it returns the iterator object:\n    // onward, for..of works only with that object, asking it for next values\n    return {\n      current: this.from,\n      last: this.to,\n\n      // next() is called on each iteration by the for..of loop\n      next() {\n        // it should return the value as an object {done:.., value :...}\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\n// iteration over range returns numbers from range.from to range.to\nalert([...range]); // 1,2,3,4,5\n```\n\nWe can use a generator function for iteration by providing it as `Symbol.iterator`.\n\nHere's the same `range`, but much more compact:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  *[Symbol.iterator]() { // a shorthand for [Symbol.iterator]: function*()\n    for(let value = this.from; value <= this.to; value++) {\n      yield value;\n    }\n  }\n};\n\nalert( [...range] ); // 1,2,3,4,5\n```\n\nThat works, because `range[Symbol.iterator]()` now returns a generator, and generator methods are exactly what `for..of` expects:\n- it has a `.next()` method\n- that returns values in the form `{value: ..., done: true/false}`\n\nThat's not a coincidence, of course. Generators were added to JavaScript language with iterators in mind, to implement them easily.\n\nThe variant with a generator is much more concise than the original iterable code of `range`, and keeps the same functionality.\n\n```smart header=\"Generators may generate values forever\"\nIn the examples above we generated finite sequences, but we can also make a generator that yields values forever. For instance, an unending sequence of pseudo-random numbers.\n\nThat surely would require a `break` (or `return`) in `for..of` over such generator. Otherwise, the loop would repeat forever and hang.\n```\n\n## Generator composition\n\nGenerator composition is a special feature of generators that allows to transparently \"embed\" generators in each other.\n\nFor instance, we have a function that generates a sequence of numbers:\n\n```js\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n```\n\nNow we'd like to reuse it to generate a more complex sequence:\n- first, digits `0..9` (with character codes 48..57),\n- followed by uppercase alphabet letters `A..Z` (character codes 65..90)\n- followed by lowercase alphabet letters `a..z` (character codes 97..122)\n\nWe can use this sequence e.g. to create passwords by selecting characters from it (could add syntax characters as well), but let's generate it first.\n\nIn a regular function, to combine results from multiple other functions, we call them, store the results, and then join at the end.\n\nFor generators, there's a special `yield*` syntax to \"embed\" (compose) one generator into another.\n\nThe composed generator:\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n\nfunction* generatePasswordCodes() {\n\n*!*\n  // 0..9\n  yield* generateSequence(48, 57);\n\n  // A..Z\n  yield* generateSequence(65, 90);\n\n  // a..z\n  yield* generateSequence(97, 122);\n*/!*\n\n}\n\nlet str = '';\n\nfor(let code of generatePasswordCodes()) {\n  str += String.fromCharCode(code);\n}\n\nalert(str); // 0..9A..Za..z\n```\n\nThe `yield*` directive *delegates* the execution to another generator. This term means that `yield* gen` iterates over the generator `gen` and transparently forwards its yields outside. As if the values were yielded by the outer generator.\n\nThe result is the same as if we inlined the code from nested generators:\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) yield i;\n}\n\nfunction* generateAlphaNum() {\n\n*!*\n  // yield* generateSequence(48, 57);\n  for (let i = 48; i <= 57; i++) yield i;\n\n  // yield* generateSequence(65, 90);\n  for (let i = 65; i <= 90; i++) yield i;\n\n  // yield* generateSequence(97, 122);\n  for (let i = 97; i <= 122; i++) yield i;\n*/!*\n\n}\n\nlet str = '';\n\nfor(let code of generateAlphaNum()) {\n  str += String.fromCharCode(code);\n}\n\nalert(str); // 0..9A..Za..z\n```\n\nA generator composition is a natural way to insert a flow of one generator into another. It doesn't use extra memory to store intermediate results.\n\n## \"yield\" is a two-way street\n\nUntil this moment, generators were similar to iterable objects, with a special syntax to generate values. But in fact they are much more powerful and flexible.\n\nThat's because `yield` is a two-way street: it not only returns the result to the outside, but also can pass the value inside the generator.\n\nTo do so, we should call `generator.next(arg)`, with an argument. That argument becomes the result of `yield`.\n\nLet's see an example:\n\n```js run\nfunction* gen() {\n*!*\n  // Pass a question to the outer code and wait for an answer\n  let result = yield \"2 + 2 = ?\"; // (*)\n*/!*\n\n  alert(result);\n}\n\nlet generator = gen();\n\nlet question = generator.next().value; // <-- yield returns the value\n\ngenerator.next(4); // --> pass the result into the generator  \n```\n\n![](genYield2.svg)\n\n1. The first call `generator.next()` should be always made without an argument (the argument is ignored if passed). It starts the execution and returns the result of the first `yield \"2+2=?\"`. At this point the generator pauses the execution, while staying on the line `(*)`.\n2. Then, as shown at the picture above, the result of `yield` gets into the `question` variable in the calling code.\n3. On `generator.next(4)`, the generator resumes, and `4` gets in as the result: `let result = 4`.\n\nPlease note, the outer code does not have to immediately call `next(4)`. It may take time. That's not a problem: the generator will wait.\n\nFor instance:\n\n```js\n// resume the generator after some time\nsetTimeout(() => generator.next(4), 1000);\n```\n\nAs we can see, unlike regular functions, a generator and the calling code can exchange results by passing values in `next/yield`.\n\nTo make things more obvious, here's another example, with more calls:\n\n```js run\nfunction* gen() {\n  let ask1 = yield \"2 + 2 = ?\";\n\n  alert(ask1); // 4\n\n  let ask2 = yield \"3 * 3 = ?\"\n\n  alert(ask2); // 9\n}\n\nlet generator = gen();\n\nalert( generator.next().value ); // \"2 + 2 = ?\"\n\nalert( generator.next(4).value ); // \"3 * 3 = ?\"\n\nalert( generator.next(9).done ); // true\n```\n\nThe execution picture:\n\n![](genYield2-2.svg)\n\n1. The first `.next()` starts the execution... It reaches the first `yield`.\n2. The result is returned to the outer code.\n3. The second `.next(4)` passes `4` back to the generator as the result of the first `yield`, and resumes the execution.\n4. ...It reaches the second `yield`, that becomes the result of the generator call.\n5. The third `next(9)` passes `9` into the generator as the result of the second `yield` and resumes the execution that reaches the end of the function, so `done: true`.\n\nIt's like a \"ping-pong\" game. Each `next(value)` (excluding the first one) passes a value into the generator, that becomes the result of the current `yield`, and then gets back the result of the next `yield`.\n\n## generator.throw\n\nAs we observed in the examples above, the outer code may pass a value into the generator, as the result of `yield`.\n\n...But it can also initiate (throw) an error there. That's natural, as an error is a kind of result.\n\nTo pass an error into a `yield`, we should call `generator.throw(err)`. In that case, the `err` is thrown in the line with that `yield`.\n\nFor instance, here the yield of `\"2 + 2 = ?\"` leads to an error:\n\n```js run\nfunction* gen() {\n  try {\n    let result = yield \"2 + 2 = ?\"; // (1)\n\n    alert(\"The execution does not reach here, because the exception is thrown above\");\n  } catch(e) {\n    alert(e); // shows the error\n  }\n}\n\nlet generator = gen();\n\nlet question = generator.next().value;\n\n*!*\ngenerator.throw(new Error(\"The answer is not found in my database\")); // (2)\n*/!*\n```\n\nThe error, thrown into the generator at line `(2)` leads to an exception in line `(1)` with `yield`. In the example above, `try..catch` catches it and shows it.\n\nIf we don't catch it, then just like any exception, it \"falls out\" the generator into the calling code.\n\nThe current line of the calling code is the line with `generator.throw`, labelled as `(2)`. So we can catch it here, like this:\n\n```js run\nfunction* generate() {\n  let result = yield \"2 + 2 = ?\"; // Error in this line\n}\n\nlet generator = generate();\n\nlet question = generator.next().value;\n\n*!*\ntry {\n  generator.throw(new Error(\"The answer is not found in my database\"));\n} catch(e) {\n  alert(e); // shows the error\n}\n*/!*\n```\n\nIf we don't catch the error there, then, as usual, it falls through to the outer calling code (if any) and, if uncaught, kills the script.\n\n## Summary\n\n- Generators are created by generator functions `function* f(…) {…}`.\n- Inside generators (only) there exists a `yield` operator.\n- The outer code and the generator may exchange results via `next/yield` calls.\n\nIn modern JavaScript, generators are rarely used. But sometimes they come in handy, because the ability of a function to exchange data with the calling code during the execution is quite unique. And, surely, they are great for making iterable objects.\n\nAlso, in the next chapter we'll learn async generators, which are used to read streams of asynchronously generated data (e.g paginated fetches over a network) in `for await ... of` loops.\n\nIn web-programming we often work with streamed data, so that's another very important use case.\n"
  },
  {
    "path": "1-js/12-generators-iterators/2-async-iterators-generators/article.md",
    "content": "# Iterasi dan generator asinkron\n\nIterasi asinkron memungkinkan kita melakukan iterasi atas data yang datang secara asinkron, sesuai permintaan. Seperti, misalnya, ketika kita mengunduh sesuatu yang sepotong demi sepotong melalui jaringan. Dan generator asinkron membuatnya lebih nyaman.\n\nMari kita lihat contoh sederhana terlebih dahulu, untuk memahami sintaksnya, lalu meninjau kasus penggunaan kehidupan nyata.\n\n## Ingat _iterable_\n\nMari kita ingat topik tentang _iterable_.\n\nIdenya adalah kita memiliki objek, seperti `range` di sini:\n\n```js\nlet range = {\n  from: 1,\n  to: 5,\n};\n```\n\n...Dan kita ingin menggunakan perulangan `for..of` di atasnya, seperti `for(value of range)`, untuk mendapatkan nilai dari `1` hingga `5`.\n\nDengan kata lain, kita ingin menambahkan kemampuan iterasi ke objek.\n\nItu bisa diimplementasikan menggunakan metode khusus dengan nama `Symbol.iterator`:\n\n- Metode ini dipanggil oleh konstruksi `for..of` ketika perulangan dimulai, dan harus mengembalikan objek dengan metode `next`.\n- Untuk setiap iterasi, metode `next()` dipanggil untuk nilai berikutnya.\n- `next()` harus mengembalikan nilai dalam bentuk `{done: true/false, value:<loop value>}`, di mana `done:true` berarti akhir dari perulangan.\n\nBerikut implementasi untuk `range` _iterable_:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n*!*\n  [Symbol.iterator]() { // dipanggil sekali, di awal for..of\n*/!*\n    return {\n      current: this.from,\n      last: this.to,\n\n*!*\n      next() { // memanggil setiap iterasi, untuk mendapatkan nilai berikutnya\n*/!*\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\nfor(let value of range) {\n  alert(value); // 1 lalu 2, lalu 3, lalu 4, lalu 5\n}\n```\n\nJika ada yang tidak jelas, silakan kunjungi bab [](info:iterable), ini memberikan semua detail tentang iterable biasa.\n\n## _Iterable_ asinkron\n\nIterasi asinkron diperlukan jika nilai muncul secara asinkron: setelah `setTimeout` atau jenis penundaan lainnya.\n\nKasus yang paling umum adalah objek perlu membuat permintaan jaringan untuk memberikan nilai berikutnya, kita akan melihat contoh kehidupan nyata nanti.\n\nUntuk membuat sebuah objek dapat diulang secara asinkron:\n\n1. Gunakan `Symbol.asyncIterator` sebagai ganti `Symbol.iterator`.\n2. Metode `next()` harus mengembalikan sebuah _promise_ (untuk dipenuhi dengan nilai berikutnya).\n   - Kata kunci `async` menanganinya, kita cukup membuat `async next() `.\n3. Untuk melakukan iterasi pada objek seperti itu, kita harus menggunakan perulangan `for await (let item of iterable)`.\n   - Perhatikan kata `await`.\n\nSebagai contoh awal, mari kita buat objek `range` yang dapat diulang, serupa seperti sebelumnya, tetapi sekarang akan mengembalikan nilai secara asinkron, satu per detik.\n\nYang perlu kita lakukan adalah melakukan beberapa penggantian pada kode di atas:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n*!*\n  [Symbol.asyncIterator]() { // (1)\n*/!*\n    return {\n      current: this.from,\n      last: this.to,\n\n*!*\n      async next() { // (2)\n*/!*\n\n*!*\n        // catatan: kita bisa menggunakan \"await\" di dalam async next:\n        await new Promise(resolve => setTimeout(resolve, 1000)); // (3)\n*/!*\n\n        if (this.current <= this.last) {\n          return { done: false, value: this.current++ };\n        } else {\n          return { done: true };\n        }\n      }\n    };\n  }\n};\n\n(async () => {\n\n*!*\n  for await (let value of range) { // (4)\n    alert(value); // 1,2,3,4,5\n  }\n*/!*\n\n})()\n```\n\nSeperti yang bisa kita lihat, strukturnya mirip dengan iterator biasa:\n\n1. Untuk membuat sebuah objek asinkron _iterable_, itu harus memiliki metode `Symbol.asyncIterator` `(1)`.\n2. Metode ini harus mengembalikan objek dengan metode `next()` mengembalikan _promise_ `(2)`.\n3. Metode `next()` tidak harus menjadi `async`, ini mungkin metode biasa yang mengembalikan sebuah _promise_, tetapi `async` memungkinkan kita untuk menggunakan `await`, jadi itu mudah. Disini kita hanya menunda sebentar `(3)`.\n4. Untuk iterasi, kita menggunakan `for await(let value of range)` `(4)`, yaitu menambahkan \"await\" setelah \"for\". Ini memanggil `range[Symbol.asyncIterator]()` sekali, dan kemudian `next()` untuk nilai.\n\nBerikut tabel kecil dengan perbedaannya:\n\n|                                         | Iterator          | Iterator Asinkron      |\n| --------------------------------------- | ----------------- | ---------------------- |\n| Metode objek untuk menyediakan iterator | `Symbol.iterator` | `Symbol.asyncIterator` |\n| Nilai kembali `next()` adalah           | nilai apapun      | `Promise`              |\n| Untuk mengulang, gunakan                | `for..of`         | `for await..of`        |\n\n````warn header=\"Sintaks _spread_ `...` tidak bekerja secara asinkron\"\nFitur yang membutuhkan iterator sinkron dan reguler, tidak berfungsi dengan yang asinkron.\n\nMisalnya, sintaks _spread_ tidak akan berfungsi:\n\n```js\nalert([...range]); // Error, no Symbol.iterator (tidak ada Symbol.iterator)\n```\n\nItu wajar, karena mengharapkan untuk menemukan `Symbol.iterator`, bukan `Symbol.asyncIterator`.\n\nIni juga kasus untuk `for..of`: sintaks tanpa `await` membutuhkan` Symbol.iterator`.\n\n`````\n\n## Ingat generator\n\nSekarang mari kita ingat generator, karena memungkinkan untuk membuat kode iterasi jauh lebih pendek. Seringkali, ketika kita ingin membuat _iterable_, kita akan menggunakan generator.\n\nUntuk kesederhanaan semata, menghilangkan beberapa hal penting, mereka adalah \"fungsi yang menghasilkan nilai\". Mereka dijelaskan secara rinci di bab [] (info:generators).\n\nGenerator diberi label dengan `function*` (catat permulaannya) dan gunakan `yield` untuk menghasilkan nilai, kemudian kita dapat menggunakan `for..of` untuk mengulanginya.\n\nContoh ini menghasilkan urutan nilai dari `start` hingga `end`:\n\n```js run\nfunction* generateSequence(start, end) {\n  for (let i = start; i <= end; i++) {\n    yield i;\n  }\n}\n\nfor(let value of generateSequence(1, 5)) {\n  alert(value); // 1, lalu 2, lalu 3, lalu 4, lalu 5\n}\n```\n\nSeperti yang telah kita ketahui, untuk membuat sebuah objek menjadi _iterable_, kita harus menambahkan `Symbol.iterator` padanya.\n\n```js\nlet range = {\n  from: 1,\n  to: 5,\n*!*\n  [Symbol.iterator]() {\n    return <objek dengan next untuk membuat range iterable>\n  }\n*/!*\n}\n```\n\nPraktik umum untuk `Symbol.iterator` adalah mengembalikan generator, ini membuat kode lebih pendek, seperti yang kamu lihat:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  *[Symbol.iterator]() { // singkatan untuk [Symbol.iterator]: function*()\n    for(let value = this.from; value <= this.to; value++) {\n      yield value;\n    }\n  }\n};\n\nfor(let value of range) {\n  alert(value); // 1, lalu 2, lalu 3, lalu 4, lalu 5\n}\n```\n\nSilakan lihat bab [](info:generators) jika kamu ingin lebih jelasnya.\n\nDi generator biasa kita tidak bisa menggunakan `await`. Semua nilai harus datang secara sinkron, seperti yang diharuskan oleh konstruksi `for..of`.\n\nBagaimana jika kita ingin menghasilkan nilai secara asinkron? Dari permintaan jaringan, misalnya.\n\nMari beralih ke generator asinkron untuk memungkinkannya.\n\n## Generator asinkron (akhirnya)\n\nUntuk sebagian besar aplikasi praktis, ketika kita ingin membuat objek yang menghasilkan urutan nilai secara asinkron, kita dapat menggunakan generator asinkron.\n\nSintaksnya sederhana: tambahkan `function*` dengan `async`. Itu membuat generator asinkron.\n\nDan kemudian gunakan `for await (...)` untuk mengulanginya, seperti ini:\n\n```js run\n*!*async*/!* function* generateSequence(start, end) {\n\n  for (let i = start; i <= end; i++) {\n\n*!*\n    // Wow, can use await!\n    await new Promise(resolve => setTimeout(resolve, 1000));\n*/!*\n\n    yield i;\n  }\n\n}\n\n(async () => {\n\n  let generator = generateSequence(1, 5);\n  for *!*await*/!* (let value of generator) {\n    alert(value); // 1, lalu 2, lalu 3, lalu 4, lalu 5 (dengan penundaan antaranya)\n  }\n\n})();\n```\n\nKarena generatornya asinkron, kita bisa menggunakan `await` di dalamnya, mengandalkan _promise_, melakukan permintaan jaringan, dan sebagainya.\n\n````smart header=\"Perbedaan didalamnya\"\nSecara teknis, jika kamu adalah pembaca tingkat lanjut yang mengingat detail tentang generator, ada perbedaan internal.\n\nUntuk generator asinkron, metode `generator.next()` adalah asinkron, yang mengembalikan _promise_.\n\nDalam generator biasa kita akan menggunakan `result = generator.next()` untuk mendapatkan nilai. Dalam generator asinkron, kita harus menambahkan `await`, seperti ini:\n\n```js\nresult = await generator.next(); // result = {value: ..., done: true/false}\n```\nItulah mengapa generator asinkron bekerja dengan `for await...of`.\n`````\n\n### Rentang _iterable_ asinkron\n\nGenerator biasa dapat digunakan sebagai `Symbol.iterator` untuk mempersingkat kode iterasi.\n\nSerupa dengan itu, generator asinkron dapat digunakan sebagai `Symbol.asyncIterator` untuk mengimplementasikan iterasi asinkron.\n\nMisalnya, kita dapat membuat objek `range` menghasilkan nilai secara asinkron, sekali per detik, dengan mengganti `Symbol.iterator` sinkron dengan `Symbol.asyncIterator` asinkron:\n\n```js run\nlet range = {\n  from: 1,\n  to: 5,\n\n  // baris ini sama dengan [Symbol.asyncIterator]: async function*() {\n*!*\n  async *[Symbol.asyncIterator]() {\n*/!*\n    for(let value = this.from; value <= this.to; value++) {\n\n      // buat jeda di antara value, tunggu sesuatu\n      await new Promise(resolve => setTimeout(resolve, 1000));\n\n      yield value;\n    }\n  }\n};\n\n(async () => {\n\n  for *!*await*/!* (let value of range) {\n    alert(value); // 1, lalu 2, lalu 3, lalu 4, lalu 5\n  }\n\n})();\n```\n\nSekarang _value_ datang dengan jeda 1 detik di antara mereka.\n\n```smart\nSecara teknis, kita bisa menambahkan `Symbol.iterator` dan `Symbol.asyncIterator` ke objek, jadi keduanya secara sinkron (`for..of`) dan asinkron (`for await..of`) _iterable_.\n\nNamun dalam praktiknya, itu akan menjadi hal yang aneh untuk dilakukan.\n```\n\n## Contoh kehidupan nyata: _paginated_ data\n\nSejauh ini kita telah melihat contoh-contoh dasar, untuk mendapatkan pemahaman. Sekarang mari kita tinjau kasus penggunaan kehidupan nyata.\n\nAda banyak layanan daring yang memberikan _paginated_ data. Misalnya, saat kita membutuhkan daftar pengguna, permintaan mengembalikan jumlah yang telah ditentukan sebelumnya (misalnya 100 pengguna) - \"satu halaman\", dan memberikan URL ke halaman berikutnya.\n\nPola ini sangat umum. Ini bukan tentang pengguna, tetapi tentang apa saja.\n\nMisalnya, GitHub memungkinkan kita untuk mengambil _commits_ dengan cara yang sama, _paginated_:\n\n- Kita harus membuat permintaan untuk `fetch` dalam formulir `https://api.github.com/repos/<repo>/commits`.\n- Ini merespons dengan JSON 30 _commits_, dan juga menyediakan tautan ke halaman berikutnya di tajuk `Link`.\n- Lalu kita bisa menggunakan tautan itu untuk permintaan berikutnya, untuk mendapatkan lebih banyak _commits_, dan seterusnya.\n\nUntuk kode kita, kita ingin memiliki cara yang lebih sederhana untuk mendapatkan _commits_.\n\nMari buat fungsi `fetchCommits(repo)` yang mendapatkan _commits_ untuk kita, membuat permintaan kapan pun diperlukan. Dan biarkan peduli tentang semua hal penomoran halaman. Bagi kita ini akan menjadi iterasi asinkron sederhana `for await..of`.\n\nJadi penggunaannya akan seperti ini:\n\n```js\nfor await (let commit of fetchCommits('username/repository')) {\n  // proses commit\n}\n```\n\nBerikut fungsi tersebut, diimplementasikan sebagai generator asinkron:\n\n```js\nasync function* fetchCommits(repo) {\n  let url = `https://api.github.com/repos/${repo}/commits`;\n\n  while (url) {\n    const response = await fetch(url, {\n      // (1)\n      headers: { 'User-Agent': 'Our script' }, // github membutuhkan header user-agent\n    });\n\n    const body = await response.json(); // (2) respon adalah JSON (senarai commits)\n\n    // (3) URL halaman berikutnya ada di header, ekstrak itu\n    let nextPage = response.headers.get('Link').match(/<(.*?)>; rel=\"next\"/);\n    nextPage = nextPage?.[1];\n\n    url = nextPage;\n\n    for (let commit of body) {\n      // (4) menghasilkan commit satu per satu, sampai halaman berakhir\n      yield commit;\n    }\n  }\n}\n```\n\nPenjelasan lebih lanjut tentang cara kerjanya:\n\n1. Kita menggunakan metode peramban [fetch](info:fetch) untuk mengunduh _commits_.\n\n   - URL awalnya adalah `https://api.github.com/repos/<repo>/commits`, dan halaman berikutnya akan berada di `Link` header tanggapan.\n   - Metode `fetch` memungkinkan kita untuk memberikan otorisasi dan tajuk lainnya jika diperlukan -- di sini GitHub memerlukan `User-Agent`.\n\n2. _commits_ dikembalikan dalam format JSON.\n3. Kita harus mendapatkan URL halaman berikutnya dari tajuk `Link` dari respon. Ini memiliki format khusus, jadi kita menggunakan ekspresi reguler untuk itu.\n   - URL halaman berikutnya mungkin terlihat seperti ini `https://api.github.com/repositories/93253246/commits?page=2`. Ini dihasilkan oleh GitHub itu sendiri.\n4. Kemudian kita menghasilkan _commits_ yang diterima satu per satu, dan ketika mereka selesai, iterasi `while(url)` berikutnya akan terpicu, membuat satu permintaan lagi.\n\nContoh penggunaan (menunjukkan penulis _commit_ di konsol):\n\n```js run\n(async () => {\n  let count = 0;\n\n  for await (const commit of fetchCommits(\n    'javascript-tutorial/en.javascript.info'\n  )) {\n    console.log(commit.author.login);\n\n    if (++count == 100) {\n      // mari berhenti di 100 commits\n      break;\n    }\n  }\n})();\n\n// Note: If you are running this in an external sandbox, you'll need to paste here the function fetchCommits described above \n```\n\nItulah yang kita inginkan.\n\nMekanisme internal permintaan _paginated_ tidak terlihat dari luar. Bagi kita, ini hanyalah generator asinkron yang mengembalikan _commits_.\n\n## Ringkasan\n\nIterator dan generator reguler berfungsi dengan baik dengan data yang tidak membutuhkan waktu untuk dibuat.\n\nSaat kita mengharapkan data datang secara asinkron, dengan penundaan, pasangan asinkronnya dapat digunakan, dan `for await..of` daripada `for..of`.\n\nPerbedaan sintaks antara asinkron dan iterator biasa:\n\n|                                   | _Iterable_                    | Asinkron _Iterable_                                           |\n| --------------------------------- | ----------------------------- | ------------------------------------------------------------- |\n| Metode untuk menyediakan iterator | `Symbol.iterator`             | `Symbol.asyncIterator`                                        |\n| Nilai kembali `next()` adalah     | `{value:…, done: true/false}` | `Promise` yang memutuskan untuk `{value:…, done: true/false}` |\n\nPerbedaan sintaks antara generator asinkron dan biasa:\n\n|                               | Generator                     | Generator asinkron                                            |\n| ----------------------------- | ----------------------------- | ------------------------------------------------------------- |\n| Deklarasi                     | `function*`                   | `async function*`                                             |\n| Nilai kembali `next()` adalah | `{value:…, done: true/false}` | `Promise` yang memutuskan untuk `{value:…, done: true/false}` |\n\nDalam pengembangan web, kita sering menemui aliran data, ketika mengalir potongan demi potongan. Misalnya, mengunduh atau mengunggah file besar.\n\nKita dapat menggunakan generator asinkron untuk memproses data tersebut. Perlu juga dicatat bahwa di beberapa lingkungan, seperti di peramban, ada juga Antarmuka Pemrograman Aplikasi (APA) lain yang disebut _Streams_, yang menyediakan antarmuka khusus untuk bekerja dengan aliran semacam itu, untuk mengubah data dan meneruskannya dari satu aliran ke aliran lain (misalnya mengunduh dari satu tempat dan segera kirim ke tempat lain).\n"
  },
  {
    "path": "1-js/12-generators-iterators/2-async-iterators-generators/head.html",
    "content": "<script>\n  async function* fetchCommits(repo) {\n    let url = `https://api.github.com/repos/${repo}/commits`;\n\n    while (url) {\n      const response = await fetch(url, {\n        headers: { 'User-Agent': 'Our script' }, // github membutuhkan header user-agent\n      });\n\n      const body = await response.json(); // mem-parsing respons sebagai JSON (senarai commits)\n\n      // URL halaman berikutnya ada di header, ekstrak itu\n      let nextPage = response.headers.get('Link').match(/<(.*?)>; rel=\"next\"/);\n      nextPage = nextPage?.[1];\n\n      url = nextPage;\n\n      // menghasilkan commit satu per satu, setelah selesai - ambil url halaman baru\n      for (let commit of body) {\n        yield commit;\n      }\n    }\n  }\n</script>\n"
  },
  {
    "path": "1-js/12-generators-iterators/index.md",
    "content": "# Generator, iterasi lanjutan\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/article.md",
    "content": "\n# Modul, Pengenalan\n\nSaat aplikasi kita berkembang menjadi lebih besar, kita ingin membaginya menjadi banyak file, yang disebut modul. Sebuah modul bisa berisi kelas atau fungsi *library* untuk tujuan spesifik. \n\nUntuk waktu yang lama, Javascript ada tanpa sintaks modul tingkat-bahasa. Hal ini tidak menjadi masalah, karena awalnya kode skrip lebih kecil dan simpel, jadi modul tidak diperlukan.\n\nTetapi akhirnya kode skrip menjadi lebih kompleks, jadi komunitas membuat berbagai cara untuk mengatur kode menjadi modul, *library* khusus untuk memuat modul sesuai permintaan.\n\nDisebut beberapa dengan nama (Untuk alasan sejarah):\n\n- [AMD](https://en.wikipedia.org/wiki/Asynchronous_module_definition) -- satu dari sistem modul yang paling kuno, awalnya diimplementasikan oleh [require.js](http://requirejs.org/).\n- [CommonJS](http://wiki.commonjs.org/wiki/Modules/1.1) -- Sistem modul yang dibuat untuk *server* Node.js.\n- [UMD](https://github.com/umdjs/umd) -- ada satu lagi sistem modul, disarankan untuk menjadi modul universal, karena cocok dengan AMD dan CommonJS.\n\nSekarang semua ini perlahan menjadi bagian dari sejarah, tetapi kita masih bisa menemukannya di kode skrip lama. \n\nSistem modul tingkat-bahasa muncul di tahun 2015, berevolusi secara bertahap sejak saat itu, dan sekarang di dukung oleh semua browser utama dan Node.js. Jadi sekarang kita akan mulai mempelajari modul Javascript modern.\n\n## Apa itu modul?\n\nModul hanya sebuah file. Satu kode skrip adalah satu modul. Sangat simpel bukan.\n\nModul bisa memuat satu sama lain dan menggunakan pengarah khusus `export` dan `import` untuk fungsi pertukaran, memanggil fungsi dari satu modul ke modul lainnya.\n\n- `export` kata kunci variabel label dan fungsi yang bisa diakses diluar modul saat ini.\n- `import` memperbolehkan impor fungsi dari modul lain.\nMisalnya, jika kita memiliki file `sayHi.js` ekspor fungsi:\n\n```js\n// 📁 sayHi.js\nexport function sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n```\n\n...Lalu file lain bisa di impor dan menggunakannya:\n\n```js\n// 📁 main.js\nimport {sayHi} from './sayHi.js';\n\nalert(sayHi); // function...\nsayHi('John'); // Hello, John!\n```\n\n`import` akan diarahkan untuk memuat modul dari *path* `sayHi.js` yang relatif dengan file saat ini, dan menetapkan fungsi yang diekspor `sayHi` pada variabel yang sesuai.\n\nMari kita jalankan contoh ini di browser.\n\nKarena modul didukung oleh kata kunci dan fitur khusus, kita harus memberi tahu browser bahwa kode skrip harus diperlakukan sebagai modul, dengan menggunakan attribut `<script type=\"module\">`.\n\nSeperti ini:\n\n[codetabs src=\"say\" height=\"140\" current=\"index.html\"]\n\nBrowser otomatis mengambil dan mengevaluasi modul impor (dan impornya jika diperlukan), dan kemudian menjalankan kode skrip.\n\n```warn header=\"Modul hanya bekerja melalui HTTP(s), dan tidak di file lokal\" Jika kamu mencoba membuka file halaman web secara lokal, melalui protokol `file://`, kamu akan mengetahui bahwa pengarahan `import/export` tidak bekerja. Gunakan *server* web lokal, seperti [server-statis](https://www.npmjs.com/package/static-*server*#getting-started) atau kemampuan \"live server\" di editor, seperti VS Code [Ekstensi live server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.Live*server*) untuk mengecek modul.\n```\n\n## Inti dari fitur modul \n\nApa perbedaan modul, dibandingkan dengan kode skrip \"biasa\"?\n\nTerdapat inti dari fitur, valid untuk browser dan javascript di sisi *server*.\n\n### Selalu menggunakan \"use strict\"\n\nModul selalu menggunakan `use strict` secara default. Misalnya untuk menetapkan variabel yang tidak dideklarasikan akan menjadi error.\n\n```html run\n<script type=\"module\">\n  a = 5; // error\n</script>\n```\n\n### Batasan level-modul\n\nSetiap modul memiliki batasan level-tinggi sendiri. Dengan kata lain, variabel level-tinggi dan fungsi dari modul tidak bisa dilihat di kode skrip lainnya.\n\nContoh dibawah ini, dua kode skrip di impor, dan `hello.js` mencoba untuk menggunakan variabel `user` yang dideklarasikan di `user.js`, maka akan gagal:\n\n[codetabs src=\"scopes\" height=\"140\" current=\"index.html\"]\n\nModul diharapkan untuk `export`  yang ingin bisa diakses dari luar `import` yang dibutuhkan.\n\nJadi kita harus impor `user.js` pada `hello.js` untuk mendapatkan fungsi yang dibutuhkan dari `user.js` daripada mengandalkan variabel global.\n\nIni variasi yang benar:\n\n[codetabs src=\"scopes-working\" height=\"140\" current=\"hello.js\"]\n\nPada browser, batasan level-tinggi independen juga ada untuk setiap `<script type=\"module\">`:\n\n```html run\n<script type=\"module\">\n  // The variable is only visible in this module script(variabel hanya terlihat di modul skrip ini)\n  let user = \"John\";\n</script>\n\n<script type=\"module\">\n  *!*\n  alert(user); // Error: user is not defined(Error: user tidak definisikan)\n  */!*\n</script>\n```\n\nJika kita benar-benar perlu untuk membuat variabel global tingkat *window*, kita akan menetapkan secara eksplisit pada `window` dan mengaksesnya sebagai `window.user`. Tetapi itu adalah pengecualian yang membutukan alasan bagus. \n\n### Kode modul dievaluasi saat pertama kali saat di impor\n\nJika modul yang sama di impor di banyak tempat yang lain, kode yang akan dijalankan hanya yang pertama saja, lalu ekspor akan di berikan pada semua modul impor.\n\nHal ini memiliki konsekuensi yang penting. Mari kita lihat dengan contoh:\n\nPertama, jika kode modul dijalankan akan memberikan efek samping, seperti menampilkan pesan, maka mengimpornya berkali-kali hanya akan memicu pesan satu kali saja: \n\n```js\n// 📁 alert.js\nalert(\"Module is evaluated!\");\n```\n\n```js\n// Import the same module from different files(impor modul yang sama dari file yang berbeda)\n\n// 📁 1.js\nimport `./alert.js`; // Module is evaluated!(modul dievaluasi)\n\n// 📁 2.js\nimport `./alert.js`; // (shows nothing)(menampilkan kosong)\n```\n\nPada praktek, modul level-tinggi banyak di gunakan untuk inisialisasi, membuat struktur data internal, dan jika kita ingin membuat sesuatu bisa digunakan kembali -- ekspor saja.\n\nSekarang, contoh yang lebih lanjut.\n\nKatakanlah, modul mengekspor sebuah objek:\n\n```js\n// 📁 admin.js\nexport let admin = {\n  name: \"John\"\n};\n```\n\nJika modul ini diimpor dari banyak file, modul hanya akan dievalusi pertama kali saja, lalu objek `admin` dibuat, dan akan diteruskan kepada semua file impor lebih lanjut lainnya.\n\nSemua file impor akan mendapatkan persis satu hanya objek `admin`:\n\n```js\n// 📁 1.js\nimport {admin} from './admin.js';\nadmin.name = \"Pete\";\n\n// 📁 2.js\nimport {admin} from './admin.js';\nalert(admin.name); // Pete\n\n*!*\n// Both 1.js and 2.js imported the same object(1.js dan 2.js mengimpor objek yang sama)\n// Changes made in 1.js are visible in 2.js(perubahan di buat di 1.js terlihat di 2.js)\n*/!*\n```\n\nJadi, mari kita ulangi -- modul hanya dijalankan satu kali, ekspor akan dihasilkan, dan kemudian dibagikan diantara impor, jadi jika sesuatu merubah objek `admin`, modul lainnya bisa melihat hal tersebut. \n\nPerilaku seperti itu mengizinkan kita untuk bisa *mengkonfigurasi* modul di impor pertama. Kita bisa mengatur properti satu kali, dan file impor lebih lanjut sudah siap.\n\nMisalnya, modul `admin.js` mungkin menyediakan fungsi tertentu, tetapi ingin mendapatkan kredensial pada objek `admin` dari luar:\n\n```js\n// 📁 admin.js\nexport let config = { };\n\nexport function sayHi() {\n  alert(`Ready to serve, ${config.user}!`);\n}\n```\n\nPada `init.js`, kode skrip pertama dari aplikasi, kita mengatur `admin.name`. Kemudian semua orang akan melihatnya, termasuk pemanggilan yang dibuat di dalam `admin.js` itu sendiri:\n\n```js\n// 📁 init.js\nimport {config} from './admin.js';\nconfig.user = \"Pete\";\n```\n\n...Sekarang modul `admin.js` telah dikonfigurasi.\n\nImportir lebih lanjut dapat memanggilnya, dan itu menunjukkan dengan benar pengguna saat ini:\n\n```js\n// 📁 another.js\nimport {sayHi} from './admin.js';\n\nsayHi(); // Ready to serve, *!*Pete*/!*!\n```\n\n\n### import.meta\n\nObjek `import.meta` berisi informasi tentang modul saat ini.\n\nKontennya tergantung dengan lingkungannya. Pada browser, berisi *url* dari kode skrip, atau *url* halaman web saat ini di dalam HTML:\n\n```html run height=0\n<script type=\"module\">\n  alert(import.meta.url); // script URL\n  // for an inline script - the URL of the current HTML-page\n</script>\n```\n\n### Dalam modul, \"this\" tidak didefinisikan\n\nIni semacam fitur yang kecil , tetapi untuk lebih lengkap kita harus menyebutkannya.\n\nDalam modul, level-tinggi `this` tidak didefinisikan.\n\nDibedakan dengan kode skrip tidak modul, dimana `this` adalah objek global:\n\n```html run height=0\n<script>\n  alert(this); // window\n</script>\n\n<script type=\"module\">\n  alert(this); // undefined\n</script>\n```\n\n## Fitur spesifik browser\n\nAda juga beberapa perbedaan spesifik browser skrip dengan `type=\"module\"` dibandingkan dengan yang biasa.\n\nKamu mungkin ingin melewati bagian ini jika kamu baru membacanya pertama kali, atau jika kamu tidak menggunakan Javascript pada browser.\n\n### Kode skrip modul di tangguhkan\n\nKode skrip modul selalu ditangguhkan, sama dengan efek attribut `defer` (dideskripsikan di bab [](info:script-async-defer)), untuk kode skrip diluar dan di dalam baris.\n\nDalam kata lain:\n- mengunggah kode skrip eksternal `<script type=\"module\" src=\"...\">` tidak menghalangi proses HTML, mereka memuat secara paralel dengan sumber daya asalnya.\n- kode skrip modul akan menunggu sampai dokumen HTML sudah siap (meski memiliki ukuran kecil dan memuat lebih cepat dari HTML), lalu dijalankan.\n- urutan relatif dari kode skrip diperlakukan: kode skrip yang pertama pada dokumen, akan dijalankan pertama.\n\nSebagai efek samping, kode skrip modul selalu melihat halaman HTML yang sudah dimuat semua, termasuk elemen HTML dibawahnya.\n\nMisalnya:\n\n```html run\n<script type=\"module\">\n*!*\n  alert(typeof button); // object: the script can 'see' the button below(object: kode skrip bisa melihat tombol dibawahnya)\n*/!*\n  // as modules are deferred, the script runs after the whole page is loaded( modul ditangguhkan, kode skrip dijalankan setelah semua halaman dimuat )\n</script>\n\nBandingkan dengan kode skrip dibawah:\n\n<script>\n*!*\n  alert(typeof button); // Error: button is undefined, the script can't see elements below(Error: tombol tidak didefinisikan, kode skrip tidak bisa melihat elemen dibawahnya)\n*/!*\n  // regular scripts run immediately, before the rest of the page is processed(kode skrip biasa dijalankan langsung, sebelum sisa dari halaman diproses)\n</script>\n\n<button id=\"button\">Button</button>\n```\n\nPerlu dicatat: Kode skrip kedua sebenarnya berjalan sebelum yang pertama! Jadi kita akan melihat `undefined` dahulu, baru kemudian `object`.\n\nItu karena modul ditangguhkan, jadi kita akan menunggu dokumen untuk diproses. Kode skrip biasa berjalan secara langsung, jadi kita akan melihat keluarannya dahulu.\n\nSaat menggunakan modul, kita harus sadar bahwa halaman HTML menampilkan apa yang di muat, dan modul Javascript berjalan setelah itu, jadi pengguna mungkin akan melihat halaman sebelum aplikasi Javascript siap. Beberapa fungsi mungkin belum bisa berjalan. Kita harus menambahkan \"indikator memuat\", atau jika tidak pastikan pengunjung tidak bingung dengan itu.\n\n### *Async* bekerja pada kode skrip satu baris\n\nUntuk kode skrip bukan modul, attribut `async` hanya bekerja pada kode skrip eksternal. Kode skrip async berjalan langsung saat sudah siap, independen dari kode skrip lain atau dokumen HTML.\n\nUntuk skrip modul, akan berjalan pada kode skrip satu baris.\n\nContoh, kode skrip satu baris dibawah ini memiliki `async`, jadi tidak perlu menunggu sesuatu.\n\nIni melakukan import (fetch `./analytics.js`) dan berjalan saat sudah siap, meski saat dokumen HTML belum siap, atau jika ada kode skrip lain yang masih tertunda.\n\nItu adalah fungsi yang bagus yang tidak bergantung pada sesuatu, seperti perhitungan, iklan, level-dokumen *event listener*.\n\n```html\n<!-- all dependencies are fetched (analytics.js), and the script runs -->\n<!-- doesn't wait for the document or other <script> tags -->\n<script *!async/!* type=\"module\">\n  import {counter} from './analytics.js';\n\n  counter.count();\n</script>\n```\n\n### Kode skrip eksternal\n\nKode eksternal yang memiliki `type=\"module\"` memiliki perbedaan pada dua aspek:\n\n1. Kode skrip eksternal dengan 'src' yang sama hanya berjalan satu kali:\n    ```html\n    <!-- the script my.js is fetched and executed only once -->\n    <script type=\"module\" src=\"my.js\"></script>\n    <script type=\"module\" src=\"my.js\"></script>\n    ```\n\n2. Kode skrip eksternal hanya diambil dari *origin* lain (contoh situs lain) membutuhkan *header* [CORS](mdn:Web/HTTP/CORS), akan dideskripsikan pada bab  <info:fetch-crossorigin>. Dengan kata lain, jika kode skrip modul diambil dari *origin* lain, maka *server remote* harus ... hader `Access-Control-Allow-Origin` mengizinkan untuk mengambil data.\n    ```html\n    <!-- another-site.com must supply Access-Control-Allow-Origin -->\n    <!-- otherwise, the script won't execute -->\n    <script type=\"module\" src=\"*!*http://another-site.com/their.js*/!*\"></script>\n    ```\n\n    Ini memastikan kemanan yang baik secara default.\n\n### Modul \"kosong\" tidak diizinkan\n\nPada Browser, `import` harus mendapatkan relatif atau *URL* mutlak. Modul tanpa *path* disebut dengan modul \"kosong\". Modul seperti ini tidak diizinkan pada `import`.\n\nMisalnya, `import` ini tidak valid:\n```js\nimport {sayHi} from 'sayHi'; // Error, \"bare\" module\n// the module must have a *path*, e.g. './sayHi.js' or wherever the module is\n```\n\nPada lingkungan tertentu, seperti Node.js atau alat pembungkus mengizinkan modul kosong, tanpa ada *path*, karena mereka memiliki cara tersendiri untuk menemukan modul dan pengait untuk menyesuaikannya. Tetapi browser masih belum mendukung modul kosong.\n\n### Kompabilitas, \"nomodule\"\n\nBrowser lama tidak mengerti `type=\"module\"`. Kode skrip dari tipe yang tidak diketahui akan diabaikan. Untuk itu, ada kemungkinan untuk menyediakan *fallback* menggunakan attribut `nomodule`:\n\n```html run\n<script type=\"module\">\n  alert(\"Runs in modern browsers\");\n</script>\n\n<script nomodule>\n  alert(\"Modern browsers know both type=module and nomodule, so skip this\")\n  alert(\"Old browsers ignore script with unknown type=module, but execute this.\");\n</script>\n```\n\n## Alat Pembangun\n\nDi dunia nyata, modul browser jarang digunakan dalam bentuk \"mentah\". Biasanya, kita membungkusnya bersama dengan alat khusus seperti [Webpack](https://webpack.js.org/) dan di *deploy* di *server* produksi.\n\nSalah satu keuntungan menggunakan pembungkus -- mereka memberikan lebih banyak kontrol bagaimana modul diselesaikan, mengizinkan modul kosong dan yang lainnya, seperti CSS/modul HTML.\n\nAlat pembangun melakukan langkah seperti ini:\n\n1. Modul \"main\", salah satu yang dimaksud untuk ditambahkan `<script type=\"module\">` pada HTML.\n2. Menganalisis dependensi: impor dan lalu impor dari impor dll.\n3. Membangun satu file dengan semua modul (banyak file, yang disesuaikan) mengubah pemanggil import asli dengan fungsi pembungkus, jadi modul akan bekerja. Modul tipe \"khusus\" seperti modul HTML/CSS juga didukung. \n4. Pada proses, transformasi dan optimisasi lain bisa diterapkan:\n    - Kode yang tidak terjangkau akan dihapus.\n    - Eksport yang tidak digunakan akan dihapus (\"tree-shaking\").\n    - Pernyataan spesifik pengembangan seperti `console` dan `debugger` akan dihapus.\n    - Sintaks Javascript *bleeding-edge* modern, mungkin bisa ditrasformasikan ke fungsi lama yang sama menggunakan [Babel](https://babeljs.io/).\n    - File hasil akan diperkecil (spasi dihapus, variabel diganti dengan nama yang singkat, dll).\n\nJika kita menggunakan alat pembungkus, maka kode skrip dibungkus bersama menjadi satu file (atau beberapa file), pernyataan `import/export` di dalam kode skrip akan diganti dengan fungsi pembungkus khusus. Jadi hasil kode skrip \"bungkusan\" tidak berisi `import/export`, tidak membutuhkan `type=\"module\"`, dan kita bisa menambahkannya pada kode skrip biasa:\n\n```html\n<!-- Assuming we got bundle.js from a tool like Webpack -->\n<script src=\"bundle.js\"></script>\n```\n\nMeskipun demikian, modul asli juga dapat digunakan. Jadi kita tidak akan menggunakan webpack disini: kamu bisa mengkonfigurasinya nanti.\n\n## Ringkasan\n\nSecara ringkas, konsep intinya adalah:\n\n1. Modul adalah file. Untuk membuat `import/export` bekerja, browser membutuhkan `<script type=\"module\">`. Modul memiliki bebarapa perbedaan:\n    - Ditangguhkan secara default.\n    - *Async* bekerja pada kode skrip satu baris.\n    - Untuk memuat kode skrip eksternal dari *origin* lain (domain/protocol/port), *header* CORS dibutuhkan.\n    - Duplikasi kode skrip eksternal akan diabaikan.\n2. Modul memiliki batasan lokal tingkat-tinggi dan fungsi pertukaran dengan `import/export` sendiri.\n3. Modul selalu menggunakan `use strict`.\n4. Kode modul dijalankan hanya satu kali. Ekspor di buat satu kali dan dibagikan diantara banyak impor.\n\nSaat kita menggunakan modul, setiap modul mengimplementasikan fungsi dan mengekspornya. Lalu kita menggunakan `import` untuk mengarahkan impor di tempat yang dibutuhkan. Browser memuat dan mengevaluasi kode skrip secara otomatis.\n\nPada produksi, orang-orang biasa menggunakan pembungkus seperti [Webpack](https://webpack.js.org) untuk membungkus modul bersama untuk kinerja dan alasan lainnya.\n\nPada bab selanjutnya kita akan melihat lebih banyak contoh tentang modul, dan bagaimana sesuatu bisa di ekspor/ diimpor."
  },
  {
    "path": "1-js/13-modules/01-modules-intro/say.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\">\n  import {sayHi} from './say.js';\n\n  document.body.innerHTML = sayHi('John');\n</script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/say.view/say.js",
    "content": "export function sayHi(user) {\n  return `Hello, ${user}!`;\n}\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/hello.js",
    "content": "import {user} from './user.js';\n\ndocument.body.innerHTML = user; // John\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\" src=\"hello.js\"></script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes-working.view/user.js",
    "content": "export let user = \"John\";\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/hello.js",
    "content": "alert(user); // no such variable (each module has independent variables)\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/index.html",
    "content": "<!doctype html>\n<script type=\"module\" src=\"user.js\"></script>\n<script type=\"module\" src=\"hello.js\"></script>\n"
  },
  {
    "path": "1-js/13-modules/01-modules-intro/scopes.view/user.js",
    "content": "let user = \"John\";\n"
  },
  {
    "path": "1-js/13-modules/02-import-export/article.md",
    "content": "# Export dan Import\n\nPerintah export dan import memiliki beberapa varian sintaks.\n\nPada artikel sebelumnya kita melihat cara penggunaan yang sederhana, sekarang mari telusuri lebih banyak contoh.\n\n## Export sebelum deklarasi\n\nKita dapat memberi label deklarasi apapus untuk diekspor dengan menempatkan `export` sebelumnya, baik itu variabel, fungsi atau kelas.\n\nMisalnya, dibawah ini semua export valid:\n\n```js\n// export sebuah array\n*!*export*/!* let months = ['Jan', 'Feb', 'Mar','Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n\n// export sebuah konstanta\n*!*export*/!* const MODULES_BECAME_STANDARD_YEAR = 2015;\n\n// export a class\n*!*export*/!* class User {\n  constructor(name) {\n    this.name = name;\n  }\n}\n```\n\n````smart header=\"Tidak ada titik koma setelah perintah export kelas/fungsi\"\nMohon diperhatikan bahwa `export` sebelum sebuah kelas atau fungsi tidak menjadikannya sebagai [ekspresi fungsi](info:function-expressions). Ini masih sebuah deklarasi fungsi meskipun sudah diekspor.\n\nKebanyakan panduan gaya penulisan Javascript tidak merekomendasikan titik koma setelah deklarasi fungsi dan kelas.\n\nItu mengapa tidak perlu menambahkan titik koma di akhir `export class` dan `export function`:\n\n```js\nexport function sayHi(user) {\n  alert(`Hello, ${user}!`);\n} *!* // tidak ada ; diakhir */!*\n```\n\n````\n\n## Export selain dari deklarasi\n\nKita juga dapat meletakkan perintah `export` secara terpisah.\n\nDi bawah ini kita mendeklarasikan terlebih dahulu, lalu kemudian melakukan ekspor:\n\n```js\n// 📁 say.js\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\nfunction sayBye(user) {\n  alert(`Bye, ${user}!`);\n}\n\n*!*\nexport {sayHi, sayBye}; // daftar variabel yang diekspor\n*/!*\n```\n\n...Atau, secara teknis kita dapat meletakkan `export` diatas fungsi juga.\n\n## Import \\*\n\nBiasanya, kita membuat daftar apa yang akan kita impor di dalam kurung kurawal `import {...}`, seperti ini:\n\n```js\n// 📁 main.js\n*!*\nimport {sayHi, sayBye} from './say.js';\n*/!*\n\nsayHi('John'); // Halo, John!\nsayBye('John'); // Selamat tinggal, John!\n```\n\nTetapi jika ada banyak yang harus diimpor, kita dapat melakukan impor semuanya sebagai sebuah objek menggunakan `import * as <obj>`. Sebagai contoh:\n\n```js\n// 📁 main.js\n*!*\nimport * as say from './say.js';\n*/!*\n\nsay.sayHi('John');\nsay.sayBye('John');\n```\n\nKesan pertama terkait \"impor semuanya\" adalah terdengar seperti sesuatu yang keren dan singkat ketika ditulis. Mengapa kita harus secara eksplisit membuat daftar apa yang perlu kita impor?\n\nJadi, ini adalah beberapa alasannya.\n\n1. Perkakas penggabung yang modern ([webpack](http://webpack.github.io) dan lainnya) menggabungkan semua modul sekaligus dan mengoptimalkannya untuk mempercepat proses pemuatan dan menghapus modul yang tidak digunakan.\n\n    Katakanlah kita menambahkan sebuah pustaka pihak ketiga `say.js` ke dalam proyek dengan banyak fungsi:\n\n    ```js\n    // 📁 say.js\n    export function sayHi() { ... }\n    export function sayBye() { ... }\n    export function becomeSilent() { ... }\n    ```\n\n    Sekarang jika kita hanya menggunakan salah satu dari fungsi `say.js` di proyek kita:\n\n    ```js\n    // 📁 main.js\n    import { sayHi } from './say.js';\n    ```\n\n    ...Maka kemudian ketika proses pengoptimalan berjalan akan melihatnya dan menghapus fungsi lainnya (yang tidak digunakan) dari kode yang digabungkan, ini membuat kode hasil penggabungan lebih kecil. Itulah yang disebut _\"tree-shaking\"_\n\n2. Mendaftarkan secara eksplisit apa yang akan diimpor dengan nama yang lebih pendek: `sayHi()` sebagai ganti dari `say.sayHi()`.\n3. Daftar import eksplisit memberikan gambaran yang lebih baik tentang struktur kode: apa yang digunakan dan dimana. Itu membuat dukungan kode dan proses refactoring lebih mudah.\n\n## Import \"as\"\n\nKita juga dapat menggunakan `as` untuk mengimpor dengan nama yang berbeda.\n\nSebagai contoh, cobalah impor `sayHi` ke dalam variabel lokal `hi` agar lebih singkat, dan impor `sayBye` sebagai `bye`:\n\n```js\n// 📁 main.js\n*!*\nimport {sayHi as hi, sayBye as bye} from './say.js';\n*/!*\n\nhi('John'); // Halo, John!\nbye('John'); // Selamat Tinggal, John!\n```\n\n## Export \"as\"\n\nSintaks serupa berlaku juga untuk `export`\n\nMari ekspor fungsi sebagai `hi` dan `bye`:\n\n```js\n// 📁 say.js\n...\nexport {sayHi as hi, sayBye as bye};\n```\n\nSekarang `hi` dan `bye` adalah nama resmi yang diekspor dan kemudian dapat digunakan dalam impor:\n\n```js\n// 📁 main.js\nimport * as say from './say.js';\n\nsay.*!*hi*/!*('John'); // Halo, John!\nsay.*!*bye*/!*('John'); // Selamat Tinggal, John!\n```\n\n## Export default\n\nDalam praktiknya, terdapat dua jenis modul.\n\n1. Modul yang berisi pustaka, paket fungsi, seperti `say.js` diatas.\n2. Modul yang mendeklarasikan sebuah entitas, misalnya modul `user.js` hanya mengekspor `class User`.\n\nKebanyakan pendekatan kedua yang lebih disukai, jadi untuk setiap `benda` berada dalam modulnya sendiri.\n\nTentu itu membutuhkan banyak berkas, karena semuanya menginginkan modulnya sendiri. Tetapi itu tidak menjadi masalah sama sekali. Sebenarnya, navigasi kode menjadi lebih mudah jika berkas diberi nama dengan baik dan terstruktur didalam direktori.\n\nModul menyediakan perintah khusus `export default` (\"ekspor bawaan\") untuk membuat cara \"satu hal per modul\" terlihat lebih baik.\n\nTambahkan `export default` sebelum entitas yang akan diekspor:\n\n```js\n// 📁 user.js\nexport *!*default*/!* class User { // tambahkan saja \"default\"\n  constructor(name) {\n    this.name = name;\n  }\n}\n```\n\nMungkin hanya ada satu `export default` dalam setiap berkas:\n\n...Dan kemudian impor tanpa menggunakan kurung kurawal:\n\n```js\n// 📁 main.js\nimport *!*User*/!* from './user.js'; // Bukan {User}, Hanya User\n\nnew User('John');\n```\n\nImpor tanpa kurung kurawal terlihat bagus. Kesalahan umum ketika memulai menggunakan modul adalah ketika sepenuhnya melupakan kurung kurawal. Jadi, ingat `import` memerlukan kurung kurawal untuk ekspor bernama dan tidak memerlukannya untuk ekspor bawaan.\n\n| Ekspor bernama            | Ekspor bawaan                     |\n| ------------------------- | --------------------------------- |\n| `export class User {...}` | `export default class User {...}` |\n| `import {User} from ...`  | `import User from ...`            |\n\nSecara teknis, kita dapat memiliki keduanya (ekspor bawaan dan ekspor bernama) dalam satu modul yang sama, tetapi pada praktiknya, orang tidak mencampurnya. Sebuah modul memiliki antara ekspor bernama atau ekspor bawaan.\n\nKarena mungkin hanya ada paling banyak satu ekspor bawaan tiap berkas, entitas yang diekspor mungkin tidak memiliki nama.\n\nMisalnya, dibawah ini adalah ekspor bawaan yang benar-benar valid:\n\n```js\nexport default class { // tidak ada nama kelas\n  constructor() { ... }\n}\n```\n\n```js\nexport default function (user) {\n    // tidak ada nama fungsi\n    alert(`Hello, ${user}!`);\n}\n```\n\n```js\n// ekspor nilai tunggal tanpa membuat variabel\nexport default ['Jan', 'Feb', 'Mar', 'Apr', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];\n```\n\nTidak memberikan nama tidak masalah, karena hanya ada satu `export default` setiap berkas, jadi `import` tanpa tanda kurung kurawal tahu apa yang perlu diimpor.\n\nTanpa `default`, ekspor seperti itu akan memberikan sebuh _error_:\n\n```js\nexport class { // Error! (selain 'default export' memerlukan sebuah nama)\n  constructor() {}\n}\n```\n\n### Penamaan \"default\"\n\nPada situasi tertentu kata kunci `default` digunakan untuk mereferensikan ekspor bawaan.\n\nMisalnya, untuk mengekspor fungsi yang terpisah dari tempat deklarasinya:\n\n```js\nfunction sayHi(user) {\n    alert(`Hello, ${user}!`);\n}\n\n// sama seperti jika menambahkan \"export default\" sebelum fungsi\nexport { sayHi as default };\n```\n\nAtau pada situasi yang lainnya, katakanlah sebuah modul `user.js` mengekspor satu hal utama \"bawaan\", dan beberapa yang dinamai (jarang terjadi, tetapi bisa saja terjadi):\n\n```js\n// 📁 user.js\nexport default class User {\n    constructor(name) {\n        this.name = name;\n    }\n}\n\nexport function sayHi(user) {\n    alert(`Hello, ${user}!`);\n}\n```\n\nBerikut cara mengimpor ekspor bawaan bersama dengan yang ekspor bernama:\n\n```js\n// 📁 main.js\nimport {*!*default as User*/!*, sayHi} from './user.js';\n\nnew User('John');\n```\n\nDan terakhir, jika mengimpor semua `*` sebagai sebuah objek, maka properti `default` sama persis dengan ekspor bawaan:\n\n```js\n// 📁 main.js\nimport * as user from './user.js';\n\nlet User = user.default; // ekspor bawaan\nnew User('John');\n```\n\n### Sebuah kata yang menentang ekspor bawaan\n\nEkspor bernama eksplisit. Mereka secara persis menyebutkan apa yang mereka impor, jadi kita bisa mendapatkan informasi itu dari mereka; itu adalah sesuatu yang bagus.\n\nEkspor bernama memaksa kita untuk menggunakan nama yang tepat untuk melakukan impor:\n\n```js\nimport { User } from './user.js';\n// impor {MyUser} tidak akan berfungsi, penamaannya haruss {User}\n```\n\n...Sedangkan untuk ekspor bawaan, kita selalu dapat memilih nama ketika mengimpor:\n\n```js\nimport User from './user.js'; // berfungsi\nimport MyUser from './user.js'; // berfungsi juga\n// Dapat melakukan impor apa saja... dan itu tetap berfungsi\n```\n\nKemungkinan anggota tim menggunakan penamaan yang berbeda untuk mengimpor hal yang sama, dan itu tidak baik.\n\nBiasanya, untuk menghindari hal tersebut dan menjaga konsistensi kode terdapat aturan bahwa variabel yang di impor harus sesuai dengan nama berkas, misalnya:\n\n```js\nimport User from './user.js';\nimport LoginForm from './loginForm.js';\nimport func from '/path/to/func.js';\n...\n```\n\nNamun, beberapa tim menganggapnya sebagai kelemahan serius dari ekspor default. Jadi, mereka lebih selalu suka menggunakan ekspor bernama. Meskipun hanya satu hal yang diekspor, itu masih diekspor dengan nama, tanpa `default`.\n\nItu juga membuat ekspor ulang (lihat di bawah) sedikit lebih mudah.\n\n## Ekspor Ulang\n\n\"Ekspor ulang\" sintaks `export ... from ...` memperbolehkann untuk mengimpor sesuatu dan segera mengekspornya kembali (memungkinkan dengan nama yang berbeda) seperti ini:\n\n```js\nexport { sayHi } from './say.js'; // ekspor ulang sayHi\n\nexport { default as User } from './user.js'; // ekspor ulang bawaan\n```\n\nKenapa hal tersebut diperlukan? mari lihat praktik penggunaannya.\n\nBayangkan kita menulis sebuah \"paket\": sebuah direktori dengan banyak modul, dengan beberapa fungsi yang diekpor ke luar (perkakas seperti NPM memungkinkan kita untuk menerbitkan dan mendistribusikan paket seperti itu), dan kebanyakan modul hanyalah \"pembantu\" untuk penggunaan internal di paket modul lainnya.\n\nStruktur berkas bisa seperti ini:\n\n```\nauth/\n    index.js\n    user.js\n    helpers.js\n    tests/\n        login.js\n    providers/\n        github.js\n        facebook.js\n        ...\n```\n\nKita ingin menunjukkan fungsionalitas paket melalui satu titik masuk, \"Berkas utama\" `auth/index.js` dapat digunakan seperti ini.\n\n```js\nimport { login, logout } from 'auth/index.js';\n```\n\nIdenya adalah bahwa orang luar (pengembang) yang menggunakan paket kita tidak boleh ikut campur dengan struktur internalnya, serta mencari berkas didalam direktori paket kita. Kita hanya mengekspor apa yang penting di dalam `auth/index.js` dan menyembunyikan sisaya dari pengintaian.\n\nKarena fungsionalitas yang diekspor sebenarnya tersebar diantara paket, kita dapat mengimpornya ke dalam `auth/index.js` dan kemudian kembali mengekspornya:\n\n```js\n// 📁 auth/index.js\n\n// impor login/logout dan kemudian segera mengekspornya kembali\nimport {login, logout} from './helpers.js';\nexport {login, logout};\n\n// impor default sebagai User kemudian mengekspornya\nimport User from './user.js';\nexport {User};\n...\n```\n\nSekarang pengguna dari paket kita dapat `import { login }` dari `\"auth/index.js\"`.\n\nSintaks `export ... from ...` hanyalah notasi pendek untuk proses impor-ekspor.\n\n```js\n// 📁 auth/index.js\n// impor login/logout dan kemudian segera mengekspornya kembali\nexport {login, logout} from './helpers.js';\n\n// impor default sebagai User kemudian mengekspornya\nexport {default as User} from './user.js';\n...\n```\n\n### Ekspor ulang ekspor bawaan\n\nEkspor bawaan memerlukan penanganan terpisah ketika melakukan ekspor ulang.\n\nMisalnya kita memiliki `user.js` dan kita ingin melakukan ekspor ulang kelas `User` tersebut:\n\n```js\n// 📁 user.js\nexport default class User {\n    // ...\n}\n```\n\n1. `export User form './user.js'` tidak dapat digunakan. Apa yang salah?... Ini adalah kesalahan sintaks!\n\n    Untuk melakukan ekspor ulang ekspor bawaan, kita harus menuliskan `export { default as User }` seperti contoh diatas.\n\n2. `export * from './user.js'` mengekspor ulang hanya ekspor bernama, tetapi mengabaikan ekspor bawaan.\n\n    Jika kita ingin melakukan ekspor ulang keduanya (ekspor bernama dan ekspor bawaan), maka diperlukan dua pernyataan seperti berikut:\n\n    ```js\n    export * from './user.js'; // ekspor ulang ekspor bernama\n    export { default } from './user.js'; // ekspor ulang ekspor bawaan\n    ```\n\nKeanehan dari proses mengekspor ulang ekspor bawaan adalah salah satu alasan mengapa beberapa pengembang tidak menyukainya.\n\n## Ringkasan\n\nBerikut merupakan semua jenis `export` yang kita bahas di artikel ini dan sebelumnya.\n\nKamu dapat mengeceknya secara mandiri dengan membacanya dan mengingat apa maksudnya:\n\nEkspor:\n\n-   Sebelum deklarasi dari sebuah kelas/fungsi/..:\n    -   `export [default] class/function/variable ...`\n-   Ekspor mandiri:\n    -   `export { x [as y], ... } from \"module\"`\n-   Ekspor ulang:\n    -   `export {x [as y], ...} from \"module\"`\n    -   `export * from \"module\"` (tidak mengekspor ulang bawaan).\n    -   `export {default [as y]} from \"module\"` (ekspor ulang bawaan).\n\nImpor:\n\n-   Ekspor bernama dari modul:\n    -   `import {x [as y], ...} from \"module\"`\n-   Ekspor bawaan:\n    -   `import x from \"module\"`\n    -   `import {default as x} from \"module\"`\n-   Semuanya:\n    -   `import * as obj from \"module\"`\n-   Impor modul (ini menjalankan kode), tetapi tidak disimpan kedalam variabel:\n    -   `import \"module\"`\n\nKita dapat meletakkan pernyataan `import/export` di bagian atas atau bawah dari skrip, itu tidak masalah.\n\nJadi secara teknis kode ini tidak dipermasalahkan:\n\n```js\nsayHi();\n\n// ...\n\nimport { sayHi } from './say.js'; // impor dibagian bawah berkas\n```\n\nDalam praktiknya, impor biasanya dilakukan pada awal sebuah berkas, tetapi itu hanya untuk memberikan kenyamanan lebih.\n\n**Harap diperhatikan bahwa pernyataan import/export tidak dapat digunakan jika didalam `{...}`.**\n\nSebuah impor bersyarat seperti ini tidak akan dapat berfungsi:\n\n```js\nif (something) {\n    import { sayHi } from './say.js'; // Error: import must be at top level (impor harus diluar pernyataan if dann { ... } sehingga kode tersebut tidak akan bekerja)\n}\n```\n\n...Tetapi bagaimana jika kita benar-benar perlu menhimpor sesuatu dengan syarat tertentu? atau pada waktu yang tepat? Seperti memuat modul berdasarkan permintaan, pada saat itu benar-benar dibutuhkan?\n\nOleh karena itu, kita akan mempelajari impor dinamis di artikel selanjutnya.\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/article.md",
    "content": "# Impor dinamis\n\nPernyataan ekspor dan impor yang kita bahas di bab sebelumnya disebut \"statis\". Sintaksnya sangat sederhana dan bersifat _strict_.\n\nPertama, kita tidak bisa membuat parameter `import` secara dinamis.\n\nJalur modul harus berupa _string_, tidak boleh berupa _function_ panggilan. Berikut contoh yang tidak akan berhasil:\n\n```js\nimport ... from *!*getModuleName()*/!*; // Error, hanya \"string\" yang diperbolehkan\n```\n\nKedua, kita tidak bisa meng-impor secara kondisional atau pada saat _run-time_:\n\n```js\nif(...) {\n  import ...; // Error, tidak diperbolehkan!\n}\n\n{\n  import ...; // Error, kita tidak bisa meng-impor di dalam block-scope\n}\n```\n\nItu karena `import`/`export` bertujuan untuk menyediakan tulang punggung untuk struktur kode. Itu hal yang baik, karena struktur kode dapat dianalisa, modul dapat dikumpulkan dan digabungkan menjadi satu _file_ dengan alat khusus, ekspor yang tidak digunakan dapat dihapus (\"tree-shaken\"). Itu memungkinkan hanya karena struktur dari impor/ekspor sederhana dan tetap.\n\nTetapi bagaimana kita bisa meng-impor modul secara dinamis, sesuai permintaan?\n\n## Ekspresi `import()`\n\nEkspresi `import(modul)` memuat modul dan mengembalikan sebuah _promise_ yang diselesaikan menjadi objek modul yang berisi semua ekspornya. Itu dapat dipanggil dari mana saja dalam kode.\n\nKita bisa menggunakannya secara dinamis di sembarang tempat kode, misalnya:\n\n```js\nlet modulePath = prompt(\"Modul mana yang ingin dimuat?\");\n\nimport(modulePath)\n  .then(obj => <module object>)\n  .catch(err => <loading error, e.g. if no such module>)\n```\n\nAtau, kita bisa menggunakan `let module = await import(modulePath)` jika di dalam _async function_.\n\nMisalnya, jika kita memiliki modul berikut `say.js`:\n\n```js\n// 📁 say.js\nexport function hi() {\n  alert(`Halo`);\n}\n\nexport function bye() {\n  alert(`Selamat tinggal`);\n}\n```\n\n...Kemudian impor dinamisnya bisa seperti ini:\n\n```js\nlet { hi, bye } = await import(\"./say.js\");\n\nhi();\nbye();\n```\n\nAtau, kalau `say.js` mempunyai ekspor _default_:\n\n```js\n// 📁 say.js\nexport default function () {\n  alert(\"Modul dimuat (ekspor default)!\");\n}\n```\n\n...Kemudian, untuk mengaksesnya, kita bisa menggunakan properti `default` dari objek modul:\n\n```js\nlet obj = await import(\"./say.js\");\nlet say = obj.default;\n// jika dalam satu baris: let {default: say} = await import('./say.js');\n\nsay();\n```\n\nBerikut contoh lengkapnya:\n\n[codetabs src=\"say\" current=\"index.html\"]\n\n```smart\nImpor dinamis berfungsi dalam skrip biasa, mereka tidak memerlukan `script type=\"module\"`.\n```\n\n```smart\nMeskipun `import()` terlihat seperti pemanggilan sebuah function, akan tetapi itu adalah sintaks khusus yang kebetulan menggunakan tanda kurung (mirip dengan `super()`).\n\nJadi, kita tidak bisa menyalin `import` ke dalam variabel atau menggunakan `call/apply` dengannya. `import` bukan sebuah function.\n```\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/say.view/index.html",
    "content": "<!DOCTYPE html>\n<script>\n  async function load() {\n    let say = await import(\"./say.js\");\n    say.hi(); // Halo\n    say.bye(); // Selamat tinggal\n    say.default(); // Modul dimuat (ekspor default)!\n  }\n</script>\n<button onclick=\"load()\">Klik aku</button>\n"
  },
  {
    "path": "1-js/13-modules/03-modules-dynamic-imports/say.view/say.js",
    "content": "export function hi() {\n  alert(`Halo`);\n}\n\nexport function bye() {\n  alert(`Selamat tinggal`);\n}\n\nexport default function () {\n  alert(\"Modul dimuat (ekspor default)!\");\n}\n"
  },
  {
    "path": "1-js/13-modules/index.md",
    "content": "\n# Modules\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/01-error-nonexisting/solution.md",
    "content": "\n```js run\nlet user = {\n  name: \"John\"\n};\n\nfunction wrap(target) {\n  return new Proxy(target, {\n    get(target, prop, receiver) {\n      if (prop in target) {\n        return Reflect.get(target, prop, receiver);\n      } else {\n        throw new ReferenceError(`Property doesn't exist: \"${prop}\"`)\n      }\n    }\n  });\n}\n\nuser = wrap(user);\n\nalert(user.name); // John\nalert(user.age); // ReferenceError: Property doesn't exist: \"age\"\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/01-error-nonexisting/task.md",
    "content": "# Error on reading non-existent property\n\nUsually, an attempt to read a non-existent property returns `undefined`.\n\nCreate a proxy that throws an error for an attempt to read of a non-existent property instead.\n\nThat can help to detect programming mistakes early.\n\nWrite a function `wrap(target)` that takes an object `target` and return a proxy that adds this functionality aspect.\n\nThat's how it should work:\n\n```js\nlet user = {\n  name: \"John\"\n};\n\nfunction wrap(target) {\n  return new Proxy(target, {\n*!*\n      /* your code */\n*/!*\n  });\n}\n\nuser = wrap(user);\n\nalert(user.name); // John\n*!*\nalert(user.age); // ReferenceError: Property doesn't exist: \"age\"\n*/!*\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/02-array-negative/solution.md",
    "content": "\n```js run\nlet array = [1, 2, 3];\n\narray = new Proxy(array, {\n  get(target, prop, receiver) {\n    if (prop < 0) {\n      // even if we access it like arr[1]\n      // prop is a string, so need to convert it to number\n      prop = +prop + target.length;\n    }\n    return Reflect.get(target, prop, receiver);\n  }\n});\n\n\nalert(array[-1]); // 3\nalert(array[-2]); // 2\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/02-array-negative/task.md",
    "content": "\n# Accessing array[-1]\n\nIn some programming languages, we can access array elements using negative indexes, counted from the end.\n\nLike this:\n\n```js\nlet array = [1, 2, 3];\n\narray[-1]; // 3, the last element\narray[-2]; // 2, one step from the end\narray[-3]; // 1, two steps from the end\n```\n\nIn other words, `array[-N]` is the same as `array[array.length - N]`.\n\nCreate a proxy to implement that behavior.\n\nThat's how it should work:\n\n```js\nlet array = [1, 2, 3];\n\narray = new Proxy(array, {\n  /* your code */\n});\n\nalert( array[-1] ); // 3\nalert( array[-2] ); // 2\n\n// Other array functionality should be kept \"as is\"\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/03-observable/solution.md",
    "content": "The solution consists of two parts:\n\n1. Whenever `.observe(handler)` is called, we need to remember the handler somewhere, to be able to call it later. We can store handlers right in the object, using our symbol as the property key.\n2. We need a proxy with `set` trap to call handlers in case of any change.\n\n```js run\nlet handlers = Symbol('handlers');\n\nfunction makeObservable(target) {\n  // 1. Initialize handlers store\n  target[handlers] = [];\n\n  // Store the handler function in array for future calls\n  target.observe = function(handler) {\n    this[handlers].push(handler);\n  };\n\n  // 2. Create a proxy to handle changes\n  return new Proxy(target, {\n    set(target, property, value, receiver) {\n      let success = Reflect.set(...arguments); // forward the operation to object\n      if (success) { // if there were no error while setting the property\n        // call all handlers\n        target[handlers].forEach(handler => handler(property, value));\n      }\n      return success;\n    }\n  });\n}\n\nlet user = {};\n\nuser = makeObservable(user);\n\nuser.observe((key, value) => {\n  alert(`SET ${key}=${value}`);\n});\n\nuser.name = \"John\";\n```\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/03-observable/task.md",
    "content": "\n# Observable\n\nCreate a function `makeObservable(target)` that \"makes the object observable\" by returning a proxy.\n\nHere's how it should work:\n\n```js run\nfunction makeObservable(target) {\n  /* your code */\n}\n\nlet user = {};\nuser = makeObservable(user);\n\nuser.observe((key, value) => {\n  alert(`SET ${key}=${value}`);\n});\n\nuser.name = \"John\"; // alerts: SET name=John\n```\n\nIn other words, an object returned by `makeObservable` is just like the original one, but also has the method `observe(handler)` that sets `handler` function to be called on any property change.\n\nWhenever a property changes, `handler(key, value)` is called with the name and value of the property.\n\nP.S. In this task, please only take care about writing to a property. Other operations can be implemented in a similar way.\n"
  },
  {
    "path": "1-js/99-js-misc/01-proxy/article.md",
    "content": "# Proxy and Reflect\n\nA `Proxy` object wraps another object and intercepts operations, like reading/writing properties and others, optionally handling them on its own, or transparently allowing the object to handle them.\n\nProxies are used in many libraries and some browser frameworks. We'll see many practical applications in this article.\n\n## Proxy\n\nThe syntax:\n\n```js\nlet proxy = new Proxy(target, handler)\n```\n\n- `target` -- is an object to wrap, can be anything, including functions.\n- `handler` -- proxy configuration: an object with \"traps\", methods that intercept operations. - e.g. `get` trap for reading a property of `target`, `set` trap for writing a property into `target`, and so on.\n\nFor operations on `proxy`, if there's a corresponding trap in `handler`, then it runs, and the proxy has a chance to handle it, otherwise the operation is performed on `target`.\n\nAs a starting example, let's create a proxy without any traps:\n\n```js run\nlet target = {};\nlet proxy = new Proxy(target, {}); // empty handler\n\nproxy.test = 5; // writing to proxy (1)\nalert(target.test); // 5, the property appeared in target!\n\nalert(proxy.test); // 5, we can read it from proxy too (2)\n\nfor(let key in proxy) alert(key); // test, iteration works (3)\n```\n\nAs there are no traps, all operations on `proxy` are forwarded to `target`.\n\n1. A writing operation `proxy.test=` sets the value on `target`.\n2. A reading operation `proxy.test` returns the value from `target`.\n3. Iteration over `proxy` returns values from `target`.\n\nAs we can see, without any traps, `proxy` is a transparent wrapper around `target`.\n\n![](proxy.svg)\n\n`Proxy` is a special \"exotic object\". It doesn't have own properties. With an empty `handler` it transparently forwards operations to `target`.\n\nTo activate more capabilities, let's add traps.\n\nWhat can we intercept with them?\n\nFor most operations on objects, there's a so-called \"internal method\" in the JavaScript specification that describes how it works at the lowest level. For instance `[[Get]]`, the internal method to read a property, `[[Set]]`, the internal method to write a property, and so on. These methods are only used in the specification, we can't call them directly by name.\n\nProxy traps intercept invocations of these methods. They are listed in the [Proxy specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots) and in the table below.\n\nFor every internal method, there's a trap in this table: the name of the method that we can add to the `handler` parameter of `new Proxy` to intercept the operation:\n\n| Internal Method | Handler Method | Triggers when... |\n|-----------------|----------------|-------------|\n| `[[Get]]` | `get` | reading a property |\n| `[[Set]]` | `set` | writing to a property |\n| `[[HasProperty]]` | `has` | `in` operator |\n| `[[Delete]]` | `deleteProperty` | `delete` operator |\n| `[[Call]]` | `apply` | function call |\n| `[[Construct]]` | `construct` | `new` operator |\n| `[[GetPrototypeOf]]` | `getPrototypeOf` | [Object.getPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/getPrototypeOf) |\n| `[[SetPrototypeOf]]` | `setPrototypeOf` | [Object.setPrototypeOf](mdn:/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) |\n| `[[IsExtensible]]` | `isExtensible` | [Object.isExtensible](mdn:/JavaScript/Reference/Global_Objects/Object/isExtensible) |\n| `[[PreventExtensions]]` | `preventExtensions` | [Object.preventExtensions](mdn:/JavaScript/Reference/Global_Objects/Object/preventExtensions) |\n| `[[DefineOwnProperty]]` | `defineProperty` | [Object.defineProperty](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperty), [Object.defineProperties](mdn:/JavaScript/Reference/Global_Objects/Object/defineProperties) |\n| `[[GetOwnProperty]]` | `getOwnPropertyDescriptor` | [Object.getOwnPropertyDescriptor](mdn:/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor), `for..in`, `Object.keys/values/entries` |\n| `[[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` |\n\n```warn header=\"Invariants\"\nJavaScript enforces some invariants -- conditions that must be fulfilled by internal methods and traps.\n\nMost of them are for return values:\n- `[[Set]]` must return `true` if the value was written successfully, otherwise `false`.\n- `[[Delete]]` must return `true` if the value was deleted successfully, otherwise `false`.\n- ...and so on, we'll see more in examples below.\n\nThere are some other invariants, like:\n- `[[GetPrototypeOf]]`, applied to the proxy object must return the same value as `[[GetPrototypeOf]]` applied to the proxy object's target object. In other words, reading prototype of a proxy must always return the prototype of the target object.\n\nTraps can intercept these operations, but they must follow these rules.\n\nInvariants ensure correct and consistent behavior of language features. The full invariants list is in [the specification](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots). You probably won't violate them if you're not doing something weird.\n```\n\nLet's see how that works in practical examples.\n\n## Default value with \"get\" trap\n\nThe most common traps are for reading/writing properties.\n\nTo intercept reading, the `handler` should have a method `get(target, property, receiver)`.\n\nIt triggers when a property is read, with following arguments:\n\n- `target` -- is the target object, the one passed as the first argument to `new Proxy`,\n- `property` -- property name,\n- `receiver` -- if the target property is a getter, then `receiver` is the object that's going to be used as `this` in its call. Usually that's the `proxy` object itself (or an object that inherits from it, if we inherit from proxy). Right now we don't need this argument, so it will be explained in more detail later.\n\nLet's use `get` to implement default values for an object.\n\nWe'll make a numeric array that returns `0` for nonexistent values.\n\nUsually when one tries to get a non-existing array item, they get `undefined`, but we'll wrap a regular array into the proxy that traps reading and returns `0` if there's no such property:\n\n```js run\nlet numbers = [0, 1, 2];\n\nnumbers = new Proxy(numbers, {\n  get(target, prop) {\n    if (prop in target) {\n      return target[prop];\n    } else {\n      return 0; // default value\n    }\n  }\n});\n\n*!*\nalert( numbers[1] ); // 1\nalert( numbers[123] ); // 0 (no such item)\n*/!*\n```\n\nAs we can see, it's quite easy to do with a `get` trap.\n\nWe can use `Proxy` to implement any logic for \"default\" values.\n\nImagine we have a dictionary, with phrases and their translations:\n\n```js run\nlet dictionary = {\n  'Hello': 'Hola',\n  'Bye': 'Adiós'\n};\n\nalert( dictionary['Hello'] ); // Hola\nalert( dictionary['Welcome'] ); // undefined\n```\n\nRight now, if there's no phrase, reading from `dictionary` returns `undefined`. But in practice, leaving a phrase untranslated is usually better than `undefined`. So let's make it return an untranslated phrase in that case instead of `undefined`.\n\nTo achieve that, we'll wrap `dictionary` in a proxy that intercepts reading operations:\n\n```js run\nlet dictionary = {\n  'Hello': 'Hola',\n  'Bye': 'Adiós'\n};\n\ndictionary = new Proxy(dictionary, {\n*!*\n  get(target, phrase) { // intercept reading a property from dictionary\n*/!*\n    if (phrase in target) { // if we have it in the dictionary\n      return target[phrase]; // return the translation\n    } else {\n      // otherwise, return the non-translated phrase\n      return phrase;\n    }\n  }\n});\n\n// Look up arbitrary phrases in the dictionary!\n// At worst, they're not translated.\nalert( dictionary['Hello'] ); // Hola\n*!*\nalert( dictionary['Welcome to Proxy']); // Welcome to Proxy (no translation)\n*/!*\n```\n\n````smart\nPlease note how the proxy overwrites the variable:\n\n```js\ndictionary = new Proxy(dictionary, ...);\n```\n\nThe proxy should totally replace the target object everywhere. No one should ever reference the target object after it got proxied. Otherwise it's easy to mess up.\n````\n\n## Validation with \"set\" trap\n\nLet's say we want an array exclusively for numbers. If a value of another type is added, there should be an error.\n\nThe `set` trap triggers when a property is written.\n\n`set(target, property, value, receiver)`:\n\n- `target` -- is the target object, the one passed as the first argument to `new Proxy`,\n- `property` -- property name,\n- `value` -- property value,\n- `receiver` -- similar to `get` trap, matters only for setter properties.\n\nThe `set` trap should return `true` if setting is successful, and `false` otherwise (triggers `TypeError`).\n\nLet's use it to validate new values:\n\n```js run\nlet numbers = [];\n\nnumbers = new Proxy(numbers, { // (*)\n*!*\n  set(target, prop, val) { // to intercept property writing\n*/!*\n    if (typeof val == 'number') {\n      target[prop] = val;\n      return true;\n    } else {\n      return false;\n    }\n  }\n});\n\nnumbers.push(1); // added successfully\nnumbers.push(2); // added successfully\nalert(\"Length is: \" + numbers.length); // 2\n\n*!*\nnumbers.push(\"test\"); // TypeError ('set' on proxy returned false)\n*/!*\n\nalert(\"This line is never reached (error in the line above)\");\n```\n\nPlease note: the built-in functionality of arrays is still working! Values are added by `push`. The `length` property auto-increases when values are added. Our proxy doesn't break anything.\n\nWe don't have to override value-adding array methods like `push` and `unshift`, and so on, to add checks in there, because internally they use the `[[Set]]` operation that's intercepted by the proxy.\n\nSo the code is clean and concise.\n\n```warn header=\"Don't forget to return `true`\"\nAs said above, there are invariants to be held.\n\nFor `set`, it must return `true` for a successful write.\n\nIf we forget to do it or return any falsy value, the operation triggers `TypeError`.\n```\n\n## Iteration with \"ownKeys\" and \"getOwnPropertyDescriptor\"\n\n`Object.keys`, `for..in` loop and most other methods that iterate over object properties use `[[OwnPropertyKeys]]` internal method (intercepted by `ownKeys` trap) to get a list of properties.\n\nSuch methods differ in details:\n- `Object.getOwnPropertyNames(obj)` returns non-symbol keys.\n- `Object.getOwnPropertySymbols(obj)` returns symbol keys.\n- `Object.keys/values()` returns non-symbol keys/values with `enumerable` flag (property flags were explained in the article <info:property-descriptors>).\n- `for..in` loops over non-symbol keys with `enumerable` flag, and also prototype keys.\n\n...But all of them start with that list.\n\nIn the example below we use `ownKeys` trap to make `for..in` loop over `user`, and also `Object.keys` and `Object.values`, to skip properties starting with an underscore `_`:\n\n```js run\nlet user = {\n  name: \"John\",\n  age: 30,\n  _password: \"***\"\n};\n\nuser = new Proxy(user, {\n*!*\n  ownKeys(target) {\n*/!*\n    return Object.keys(target).filter(key => !key.startsWith('_'));\n  }\n});\n\n// \"ownKeys\" filters out _password\nfor(let key in user) alert(key); // name, then: age\n\n// same effect on these methods:\nalert( Object.keys(user) ); // name,age\nalert( Object.values(user) ); // John,30\n```\n\nSo far, it works.\n\nAlthough, if we return a key that doesn't exist in the object, `Object.keys` won't list it:\n\n```js run\nlet user = { };\n\nuser = new Proxy(user, {\n*!*\n  ownKeys(target) {\n*/!*\n    return ['a', 'b', 'c'];\n  }\n});\n\nalert( Object.keys(user) ); // <empty>\n```\n\nWhy? The reason is simple: `Object.keys` returns only properties with the `enumerable` flag. To check for it, it calls the internal method `[[GetOwnProperty]]` for every property to get [its descriptor](info:property-descriptors). And here, as there's no property, its descriptor is empty, no `enumerable` flag, so it's skipped.\n\nFor `Object.keys` to return a property, we need it to either exist in the object, with the `enumerable` flag, or we can intercept calls to `[[GetOwnProperty]]` (the trap `getOwnPropertyDescriptor` does it), and return a descriptor with `enumerable: true`.\n\nHere's an example of that:\n\n```js run\nlet user = { };\n\nuser = new Proxy(user, {\n  ownKeys(target) { // called once to get a list of properties\n    return ['a', 'b', 'c'];\n  },\n\n  getOwnPropertyDescriptor(target, prop) { // called for every property\n    return {\n      enumerable: true,\n      configurable: true\n      /* ...other flags, probable \"value:...\" */\n    };\n  }\n\n});\n\nalert( Object.keys(user) ); // a, b, c\n```\n\nLet's note once again: we only need to intercept `[[GetOwnProperty]]` if the property is absent in the object.\n\n## Protected properties with \"deleteProperty\" and other traps\n\nThere's a widespread convention that properties and methods prefixed by an underscore `_` are internal. They shouldn't be accessed from outside the object.\n\nTechnically that's possible though:\n\n```js run\nlet user = {\n  name: \"John\",\n  _password: \"secret\"\n};\n\nalert(user._password); // secret\n```\n\nLet's use proxies to prevent any access to properties starting with `_`.\n\nWe'll need the traps:\n- `get` to throw an error when reading such property,\n- `set` to throw an error when writing,\n- `deleteProperty` to throw an error when deleting,\n- `ownKeys` to exclude properties starting with `_` from `for..in` and methods like `Object.keys`.\n\nHere's the code:\n\n```js run\nlet user = {\n  name: \"John\",\n  _password: \"***\"\n};\n\nuser = new Proxy(user, {\n*!*\n  get(target, prop) {\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    }\n    let value = target[prop];\n    return (typeof value === 'function') ? value.bind(target) : value; // (*)\n  },\n*!*\n  set(target, prop, val) { // to intercept property writing\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    } else {\n      target[prop] = val;\n      return true;\n    }\n  },\n*!*\n  deleteProperty(target, prop) { // to intercept property deletion\n*/!*\n    if (prop.startsWith('_')) {\n      throw new Error(\"Access denied\");\n    } else {\n      delete target[prop];\n      return true;\n    }\n  },\n*!*\n  ownKeys(target) { // to intercept property list\n*/!*\n    return Object.keys(target).filter(key => !key.startsWith('_'));\n  }\n});\n\n// \"get\" doesn't allow to read _password\ntry {\n  alert(user._password); // Error: Access denied\n} catch(e) { alert(e.message); }\n\n// \"set\" doesn't allow to write _password\ntry {\n  user._password = \"test\"; // Error: Access denied\n} catch(e) { alert(e.message); }\n\n// \"deleteProperty\" doesn't allow to delete _password\ntry {\n  delete user._password; // Error: Access denied\n} catch(e) { alert(e.message); }\n\n// \"ownKeys\" filters out _password\nfor(let key in user) alert(key); // name\n```\n\nPlease note the important detail in the `get` trap, in the line `(*)`:\n\n```js\nget(target, prop) {\n  // ...\n  let value = target[prop];\n*!*\n  return (typeof value === 'function') ? value.bind(target) : value; // (*)\n*/!*\n}\n```\n\nWhy do we need a function to call `value.bind(target)`?\n\nThe reason is that object methods, such as `user.checkPassword()`, must be able to access `_password`:\n\n```js\nuser = {\n  // ...\n  checkPassword(value) {\n    // object method must be able to read _password\n    return value === this._password;\n  }\n}\n```\n\n\nA call to `user.checkPassword()` gets proxied `user` as `this` (the object before dot becomes `this`), so when it tries to access `this._password`, the `get` trap activates (it triggers on any property read) and throws an error.\n\nSo we bind the context of object methods to the original object, `target`, in the line `(*)`. Then their future calls will use `target` as `this`, without any traps.\n\nThat solution usually works, but isn't ideal, as a method may pass the unproxied object somewhere else, and then we'll get messed up: where's the original object, and where's the proxied one?\n\nBesides, an object may be proxied multiple times (multiple proxies may add different \"tweaks\" to the object), and if we pass an unwrapped object to a method, there may be unexpected consequences.\n\nSo, such a proxy shouldn't be used everywhere.\n\n```smart header=\"Private properties of a class\"\nModern JavaScript engines natively support private properties in classes, prefixed with `#`. They are described in the article <info:private-protected-properties-methods>. No proxies required.\n\nSuch properties have their own issues though. In particular, they are not inherited.\n```\n\n## \"In range\" with \"has\" trap\n\nLet's see more examples.\n\nWe have a range object:\n\n```js\nlet range = {\n  start: 1,\n  end: 10\n};\n```\n\nWe'd like to use the `in` operator to check that a number is in `range`.\n\nThe `has` trap intercepts `in` calls.\n\n`has(target, property)`\n\n- `target` -- is the target object, passed as the first argument to `new Proxy`,\n- `property` -- property name\n\nHere's the demo:\n\n```js run\nlet range = {\n  start: 1,\n  end: 10\n};\n\nrange = new Proxy(range, {\n*!*\n  has(target, prop) {\n*/!*\n    return prop >= target.start && prop <= target.end;\n  }\n});\n\n*!*\nalert(5 in range); // true\nalert(50 in range); // false\n*/!*\n```\n\nNice syntactic sugar, isn't it? And very simple to implement.\n\n## Wrapping functions: \"apply\" [#proxy-apply]\n\nWe can wrap a proxy around a function as well.\n\nThe `apply(target, thisArg, args)` trap handles calling a proxy as function:\n\n- `target` is the target object (function is an object in JavaScript),\n- `thisArg` is the value of `this`.\n- `args` is a list of arguments.\n\nFor example, let's recall `delay(f, ms)` decorator, that we did in the article <info:call-apply-decorators>.\n\nIn that article we did it without proxies. A call to `delay(f, ms)` returned a function that forwards all calls to `f` after `ms` milliseconds.\n\nHere's the previous, function-based implementation:\n\n```js run\nfunction delay(f, ms) {\n  // return a wrapper that passes the call to f after the timeout\n  return function() { // (*)\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\n// after this wrapping, calls to sayHi will be delayed for 3 seconds\nsayHi = delay(sayHi, 3000);\n\nsayHi(\"John\"); // Hello, John! (after 3 seconds)\n```\n\nAs we've seen already, that mostly works. The wrapper function `(*)` performs the call after the timeout.\n\nBut a wrapper function does not forward property read/write operations or anything else. After the wrapping, the access is lost to properties of the original functions, such as `name`, `length` and others:\n\n```js run\nfunction delay(f, ms) {\n  return function() {\n    setTimeout(() => f.apply(this, arguments), ms);\n  };\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\n*!*\nalert(sayHi.length); // 1 (function length is the arguments count in its declaration)\n*/!*\n\nsayHi = delay(sayHi, 3000);\n\n*!*\nalert(sayHi.length); // 0 (in the wrapper declaration, there are zero arguments)\n*/!*\n```\n\n`Proxy` is much more powerful, as it forwards everything to the target object.\n\nLet's use `Proxy` instead of a wrapping function:\n\n```js run\nfunction delay(f, ms) {\n  return new Proxy(f, {\n    apply(target, thisArg, args) {\n      setTimeout(() => target.apply(thisArg, args), ms);\n    }\n  });\n}\n\nfunction sayHi(user) {\n  alert(`Hello, ${user}!`);\n}\n\nsayHi = delay(sayHi, 3000);\n\n*!*\nalert(sayHi.length); // 1 (*) proxy forwards \"get length\" operation to the target\n*/!*\n\nsayHi(\"John\"); // Hello, John! (after 3 seconds)\n```\n\nThe result is the same, but now not only calls, but all operations on the proxy are forwarded to the original function. So `sayHi.length` is returned correctly after the wrapping in the line `(*)`.\n\nWe've got a \"richer\" wrapper.\n\nOther traps exist: the full list is in the beginning of this article. Their usage pattern is similar to the above.\n\n## Reflect\n\n`Reflect` is a built-in object that simplifies creation of `Proxy`.\n\nIt was said previously that internal methods, such as `[[Get]]`, `[[Set]]` and others are specification-only, they can't be called directly.\n\nThe `Reflect` object makes that somewhat possible. Its methods are minimal wrappers around the internal methods.\n\nHere are examples of operations and `Reflect` calls that do the same:\n\n| Operation |  `Reflect` call | Internal method |\n|-----------------|----------------|-------------|\n| `obj[prop]` | `Reflect.get(obj, prop)` | `[[Get]]` |\n| `obj[prop] = value` | `Reflect.set(obj, prop, value)` | `[[Set]]` |\n| `delete obj[prop]` | `Reflect.deleteProperty(obj, prop)` | `[[Delete]]` |\n| `new F(value)` | `Reflect.construct(F, value)` | `[[Construct]]` |\n| ... | ... | ... |\n\nFor example:\n\n```js run\nlet user = {};\n\nReflect.set(user, 'name', 'John');\n\nalert(user.name); // John\n```\n\nIn particular, `Reflect` allows us to call operators (`new`, `delete`...) as functions (`Reflect.construct`, `Reflect.deleteProperty`, ...). That's an interesting capability, but here another thing is important.\n\n**For every internal method, trappable by `Proxy`, there's a corresponding method in `Reflect`, with the same name and arguments as the `Proxy` trap.**\n\nSo we can use `Reflect` to forward an operation to the original object.\n\nIn this example, both traps `get` and `set` transparently (as if they didn't exist) forward reading/writing operations to the object, showing a message:\n\n```js run\nlet user = {\n  name: \"John\",\n};\n\nuser = new Proxy(user, {\n  get(target, prop, receiver) {\n    alert(`GET ${prop}`);\n*!*\n    return Reflect.get(target, prop, receiver); // (1)\n*/!*\n  },\n  set(target, prop, val, receiver) {\n    alert(`SET ${prop}=${val}`);\n*!*\n    return Reflect.set(target, prop, val, receiver); // (2)\n*/!*\n  }\n});\n\nlet name = user.name; // shows \"GET name\"\nuser.name = \"Pete\"; // shows \"SET name=Pete\"\n```\n\nHere:\n\n- `Reflect.get` reads an object property.\n- `Reflect.set` writes an object property and returns `true` if successful, `false` otherwise.\n\nThat is, everything's simple: if a trap wants to forward the call to the object, it's enough to call `Reflect.<method>` with the same arguments.\n\nIn most cases we can do the same without `Reflect`, for instance, reading a property `Reflect.get(target, prop, receiver)` can be replaced by `target[prop]`. There are important nuances though.\n\n### Proxying a getter\n\nLet's see an example that demonstrates why `Reflect.get` is better. And we'll also see why `get/set` have the third argument `receiver`, that we didn't use before.\n\nWe have an object `user` with `_name` property and a getter for it.\n\nHere's a proxy around it:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\n*!*\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) {\n    return target[prop];\n  }\n});\n*/!*\n\nalert(userProxy.name); // Guest\n```\n\nThe `get` trap is \"transparent\" here, it returns the original property, and doesn't do anything else. That's enough for our example.\n\nEverything seems to be all right. But let's make the example a little bit more complex.\n\nAfter inheriting another object `admin` from `user`, we can observe the incorrect behavior:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) {\n    return target[prop]; // (*) target = user\n  }\n});\n\n*!*\nlet admin = {\n  __proto__: userProxy,\n  _name: \"Admin\"\n};\n\n// Expected: Admin\nalert(admin.name); // outputs: Guest (?!?)\n*/!*\n```\n\nReading `admin.name` should return `\"Admin\"`, not `\"Guest\"`!\n\nWhat's the matter? Maybe we did something wrong with the inheritance?\n\nBut if we remove the proxy, then everything will work as expected.\n\nThe problem is actually in the proxy, in the line `(*)`.\n\n1. When we read `admin.name`, as `admin` object doesn't have such own property, the search goes to its prototype.\n2. The prototype is `userProxy`.\n3. When reading `name` property from the proxy, its `get` trap triggers and returns it from the original object as `target[prop]` in the line `(*)`.\n\n    A call to `target[prop]`, when `prop` is a getter, runs its code in the context `this=target`. So the result is `this._name` from the original object `target`, that is: from `user`.\n\nTo fix such situations, we need `receiver`, the third argument of `get` trap. It keeps the correct `this` to be passed to a getter. In our case that's `admin`.\n\nHow to pass the context for a getter? For a regular function we could use `call/apply`, but that's a getter, it's not \"called\", just accessed.\n\n`Reflect.get` can do that. Everything will work right if we use it.\n\nHere's the corrected variant:\n\n```js run\nlet user = {\n  _name: \"Guest\",\n  get name() {\n    return this._name;\n  }\n};\n\nlet userProxy = new Proxy(user, {\n  get(target, prop, receiver) { // receiver = admin\n*!*\n    return Reflect.get(target, prop, receiver); // (*)\n*/!*\n  }\n});\n\n\nlet admin = {\n  __proto__: userProxy,\n  _name: \"Admin\"\n};\n\n*!*\nalert(admin.name); // Admin\n*/!*\n```\n\nNow `receiver` that keeps a reference to the correct `this` (that is `admin`), is passed to the getter using `Reflect.get` in the line `(*)`.\n\nWe can rewrite the trap even shorter:\n\n```js\nget(target, prop, receiver) {\n  return Reflect.get(*!*...arguments*/!*);\n}\n```\n\n\n`Reflect` calls are named exactly the same way as traps and accept the same arguments. They were specifically designed this way.\n\nSo, `return Reflect...` provides a safe no-brainer to forward the operation and make sure we don't forget anything related to that.\n\n## Proxy limitations\n\nProxies provide a unique way to alter or tweak the behavior of the existing objects at the lowest level. Still, it's not perfect. There are limitations.\n\n### Built-in objects: Internal slots\n\nMany built-in objects, for example `Map`, `Set`, `Date`, `Promise` and others make use of so-called \"internal slots\".\n\nThese are like properties, but reserved for internal, specification-only purposes. For instance, `Map` stores items in the internal slot `[[MapData]]`. Built-in methods access them directly, not via `[[Get]]/[[Set]]` internal methods. So `Proxy` can't intercept that.\n\nWhy care? They're internal anyway!\n\nWell, here's the issue. After a built-in object like that gets proxied, the proxy doesn't have these internal slots, so built-in methods will fail.\n\nFor example:\n\n```js run\nlet map = new Map();\n\nlet proxy = new Proxy(map, {});\n\n*!*\nproxy.set('test', 1); // Error\n*/!*\n```\n\nInternally, a `Map` stores all data in its `[[MapData]]` internal slot. The proxy doesn't have such a slot. The [built-in method `Map.prototype.set`](https://tc39.es/ecma262/#sec-map.prototype.set) method tries to access the internal property `this.[[MapData]]`, but because `this=proxy`, can't find it in `proxy` and just fails.\n\nFortunately, there's a way to fix it:\n\n```js run\nlet map = new Map();\n\nlet proxy = new Proxy(map, {\n  get(target, prop, receiver) {\n    let value = Reflect.get(...arguments);\n*!*\n    return typeof value == 'function' ? value.bind(target) : value;\n*/!*\n  }\n});\n\nproxy.set('test', 1);\nalert(proxy.get('test')); // 1 (works!)\n```\n\nNow it works fine, because `get` trap binds function properties, such as `map.set`, to the target object (`map`) itself.\n\nUnlike the previous example, the value of `this` inside `proxy.set(...)` will be not `proxy`, but the original `map`. So when the internal implementation of `set` tries to access `this.[[MapData]]` internal slot, it succeeds.\n\n```smart header=\"`Array` has no internal slots\"\nA notable exception: built-in `Array` doesn't use internal slots. That's for historical reasons, as it appeared so long ago.\n\nSo there's no such problem when proxying an array.\n```\n\n### Private fields\n\nA similar thing happens with private class fields.\n\nFor example, `getName()` method accesses the private `#name` property and breaks after proxying:\n\n```js run\nclass User {\n  #name = \"Guest\";\n\n  getName() {\n    return this.#name;\n  }\n}\n\nlet user = new User();\n\nuser = new Proxy(user, {});\n\n*!*\nalert(user.getName()); // Error\n*/!*\n```\n\nThe reason is that private fields are implemented using internal slots. JavaScript does not use `[[Get]]/[[Set]]` when accessing them.\n\nIn the call `getName()` the value of `this` is the proxied `user`, and it doesn't have the slot with private fields.\n\nOnce again, the solution with binding the method makes it work:\n\n```js run\nclass User {\n  #name = \"Guest\";\n\n  getName() {\n    return this.#name;\n  }\n}\n\nlet user = new User();\n\nuser = new Proxy(user, {\n  get(target, prop, receiver) {\n    let value = Reflect.get(...arguments);\n    return typeof value == 'function' ? value.bind(target) : value;\n  }\n});\n\nalert(user.getName()); // Guest\n```\n\nThat said, the solution has drawbacks, as explained previously: it exposes the original object to the method, potentially allowing it to be passed further and breaking other proxied functionality.\n\n### Proxy != target\n\nThe proxy and the original object are different objects. That's natural, right?\n\nSo if we use the original object as a key, and then proxy it, then the proxy can't be found:\n\n```js run\nlet allUsers = new Set();\n\nclass User {\n  constructor(name) {\n    this.name = name;\n    allUsers.add(this);\n  }\n}\n\nlet user = new User(\"John\");\n\nalert(allUsers.has(user)); // true\n\nuser = new Proxy(user, {});\n\n*!*\nalert(allUsers.has(user)); // false\n*/!*\n```\n\nAs we can see, after proxying we can't find `user` in the set `allUsers`, because the proxy is a different object.\n\n```warn header=\"Proxies can't intercept a strict equality test `===`\"\nProxies can intercept many operators, such as `new` (with `construct`), `in` (with `has`), `delete` (with `deleteProperty`) and so on.\n\nBut there's no way to intercept a strict equality test for objects. An object is strictly equal to itself only, and no other value.\n\nSo all operations and built-in classes that compare objects for equality will differentiate between the object and the proxy. No transparent replacement here.\n```\n\n## Revocable proxies\n\nA *revocable* proxy is a proxy that can be disabled.\n\nLet's say we have a resource, and would like to close access to it any moment.\n\nWhat we can do is to wrap it into a revocable proxy, without any traps. Such a proxy will forward operations to object, and we can disable it at any moment.\n\nThe syntax is:\n\n```js\nlet {proxy, revoke} = Proxy.revocable(target, handler)\n```\n\nThe call returns an object with the `proxy` and `revoke` function to disable it.\n\nHere's an example:\n\n```js run\nlet object = {\n  data: \"Valuable data\"\n};\n\nlet {proxy, revoke} = Proxy.revocable(object, {});\n\n// pass the proxy somewhere instead of object...\nalert(proxy.data); // Valuable data\n\n// later in our code\nrevoke();\n\n// the proxy isn't working any more (revoked)\nalert(proxy.data); // Error\n```\n\nA call to `revoke()` removes all internal references to the target object from the proxy, so they are no longer connected. \n\nInitially, `revoke` is separate from `proxy`, so that we can pass `proxy` around while leaving `revoke` in the current scope.\n\nWe can also bind `revoke` method to proxy by setting `proxy.revoke = revoke`.\n\nAnother option is to create a `WeakMap` that has `proxy` as the key and the corresponding `revoke` as the value, that allows to easily find `revoke` for a proxy:\n\n```js run\n*!*\nlet revokes = new WeakMap();\n*/!*\n\nlet object = {\n  data: \"Valuable data\"\n};\n\nlet {proxy, revoke} = Proxy.revocable(object, {});\n\nrevokes.set(proxy, revoke);\n\n// ..somewhere else in our code..\nrevoke = revokes.get(proxy);\nrevoke();\n\nalert(proxy.data); // Error (revoked)\n```\n\nWe use `WeakMap` instead of `Map` here because it won't block garbage collection. If a proxy object becomes \"unreachable\" (e.g. no variable references it any more), `WeakMap` allows it to be wiped from memory together with its `revoke` that we won't need any more.\n\n## References\n\n- Specification: [Proxy](https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots).\n- MDN: [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy).\n\n## Summary\n\n`Proxy` is a wrapper around an object, that forwards operations on it to the object, optionally trapping some of them.\n\nIt can wrap any kind of object, including classes and functions.\n\nThe syntax is:\n\n```js\nlet proxy = new Proxy(target, {\n  /* traps */\n});\n```\n\n...Then we should use `proxy` everywhere instead of `target`. A proxy doesn't have its own properties or methods. It traps an operation if the trap is provided, otherwise forwards it to `target` object.\n\nWe can trap:\n- Reading (`get`), writing (`set`), deleting (`deleteProperty`) a property (even a non-existing one).\n- Calling a function (`apply` trap).\n- The `new` operator (`construct` trap).\n- Many other operations (the full list is at the beginning of the article and in the [docs](mdn:/JavaScript/Reference/Global_Objects/Proxy)).\n\nThat allows us to create \"virtual\" properties and methods, implement default values, observable objects, function decorators and so much more.\n\nWe can also wrap an object multiple times in different proxies, decorating it with various aspects of functionality.\n\nThe [Reflect](mdn:/JavaScript/Reference/Global_Objects/Reflect) API is designed to complement [Proxy](mdn:/JavaScript/Reference/Global_Objects/Proxy). For any `Proxy` trap, there's a `Reflect` call with same arguments. We should use those to forward calls to target objects.\n\nProxies have some limitations:\n\n- Built-in objects have \"internal slots\", access to those can't be proxied. See the workaround above.\n- The same holds true for private class fields, as they are internally implemented using slots. So proxied method calls must have the target object as `this` to access them.\n- Object equality tests `===` can't be intercepted.\n- Performance: benchmarks depend on an engine, but generally accessing a property using a simplest proxy takes a few times longer. In practice that only matters for some \"bottleneck\" objects though.\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/1-eval-calculator/solution.md",
    "content": "Mari gunakan `eval` untuk menghitung rumus matematika:\n\n```js demo run\nlet expr = prompt(\"Type an arithmetic expression?\", '2*3+2');\n\nalert( eval(expr) );\n```\n\nPengguna dapat memasukkan teks atau kode apa pun.\n\nUntuk membuat semuanya aman dan membatasinya hanya untuk operasi aritmatika, kita dapat memeriksa `expr` menggunakan [_regular expression_](info:regular-expressions), sehingga hanya dapat berisi angka dan operator.\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/1-eval-calculator/task.md",
    "content": "importance: 4\n\n---\n\n# Kalkulator-_eval_\n\nBuatlah kalkulator yang meminta operasi aritmatika dan mengembalikan hasilnya.\n\nTidak perlu memeriksa kebenaran operasi dalam tugas ini. Cukup evaluasi dan kembalikan hasilnya.\n\n[demo]\n"
  },
  {
    "path": "1-js/99-js-misc/02-eval/article.md",
    "content": "# Eval: menjalankan kode dari _string_\n\nFungsi bawaan `eval` memungkinkan kita untuk menjalankan kode dari sebuah _string_.\n\nSintaksnya adalah:\n\n```js\nlet result = eval(code);\n```\n\nSebagai contoh:\n\n```js run\nlet code = 'alert(\"Halo\")';\neval(code); // Halo\n```\n\nSebuah kode yang berupa _string_ bisa panjang, berupa deklarasi fungsi, variabel dan lain-lain.\n\nHasil dari `eval` adalah hasil dari peryataan terakhir.\n\nSebagai contoh:\n```js run\nlet value = eval('1+1');\nalert(value); // 2\n```\n\n```js run\nlet value = eval('let i = 0; ++i');\nalert(value); // 1\n```\nKode yang dievaluasi akan dieksekusi di lingkungan leksikal saat ini, sehingga dapat melihat variabel luar:\n\n```js run no-beautify\nlet a = 1;\n\nfunction f() {\n  let a = 2;\n\n*!*\n  eval('alert(a)'); // 2\n*/!*\n}\n\nf();\n```\n\nItu juga dapat mengubah variabel luar:\n\n```js untrusted refresh run\nlet x = 5;\neval(\"x = 10\");\nalert(x); // 10, nilai diubah\n```\n\nDalam mode ketat, `eval` memiliki lingkungan leksikal sendiri. Jadi, fungsi dan variabel yang dideklarasikan di dalam eval, tidak akan terlihat di luar:\n\n```js untrusted refresh run\n// pengingat: dalam contoh yang dijalankan 'use strict' diaktifkan secara bawaan\n\neval(\"let x = 5; function f() {}\");\n\nalert(typeof x); // undefined (tidak ada variabel)\n// fungsi f juga tidak terlihat\n```\n\nTanpa `use strict`, `eval` tidak memiliki lingkungan leksikal sendiri, jadi kita akan melihat `x` dan `f` di luar.\n\n## Menggunakan \"eval\"\n\nDalam pemrograman modern `eval` jarang digunakan. Sering dikatakan bahwa \"_eval is evil_\" atau \"`eval` itu jahat\".\n\nAlasannya sederhana: dulu JavaScript adalah bahasa yang jauh lebih lemah, banyak hal yang hanya bisa dilakukan dengan `eval`. Tapi waktu itu telah berlalu satu dekade yang lalu.\n\nSekarang, hampir tidak ada alasan untuk menggunakan `eval`. Jika seseorang menggunakannya, ada kemungkinan mereka dapat menggantinya dengan konstruksi bahasa modern atau [JavaScript Module](info:modules).\n\nHarap dicatat bahwa kemampuannya untuk mengakses variabel luar memiliki efek samping.\n\n_Code minifiers_ (alat yang digunakan sebelum JS masuk ke produksi, untuk mengkompresnya) mengubah nama variabel lokal menjadi lebih pendek (seperti `a`, `b` dll) untuk membuat kode menjadi lebih kecil. Biasanya itu aman, tetapi tidak jika `eval` digunakan, karena variabel lokal dapat diakses dari kode yang dievaluasi dari _string_. Jadi _minifiers_ tidak melakukan itu untuk mengganti nama semua variabel yang terlihat dari `eval`. Itu berdampak negatif pada rasio kompresi kode.\n\nMenggunakan variabel lokal luar di dalam `eval` juga dianggap sebagai praktik pemrograman yang buruk, karena membuat kode lebih sulit dipertahankan.\n\nAda dua cara untuk terhindar dan aman dari masalah seperti itu.\n\n**Jika kode yang dievaluasi tidak menggunakan variabel luar, panggil `eval` sebagai `window.eval(...)`:**\n\nDengan cara ini, kode akan dijalankan dalam lingkup global:\n\n```js untrusted refresh run\nlet x = 1;\n{\n  let x = 5;\n  window.eval('alert(x)'); // 1 (variabel global)\n}\n```\n\n**Jika kode yang dievaluasi membutuhkan variabel lokal, ubah `eval` menjadi `new Function` dan teruskan sebagai argumen:**\n\n```js run\nlet f = new Function('a', 'alert(a)');\n\nf(5); // 5\n```\n\nKonstruksi `new Function` dijelaskan dalam bab <info:new-function>. Itu membuat fungsi baru dari sebuah _string_ dan juga dalam lingkup global. Jadi tidak bisa melihat variabel lokal. Tetapi jauh lebih jelas jika meneruskannya sebagai argumen secara eksplisit, seperti pada contoh di atas.\n\n## Ringkasan\n\nPemanggilan `eval(code)` menjalankan kode dari sebuah _string_ dan mengembalikan hasil dari pernyataan terakhir.\n- Jarang digunakan dalam JavaScript modern, karena biasanya tidak diperlukan.\n- Dapat mengakses variabel lokal luar. Itu dianggap praktik yang buruk.\n- Sebaga gantinya, untuk `eval` sebuah kode dalam lingkup global, gunakan `window.eval(code)`.\n- Atau, jika kode Anda memerlukan beberapa data dari cakupan luar, gunakan `new Function` dan teruskan sebagai argumen.\n"
  },
  {
    "path": "1-js/99-js-misc/03-currying-partials/article.md",
    "content": "libs:\n  - lodash\n\n---\n\n# _Currying_\n\n[_Currying_](https://en.wikipedia.org/wiki/Currying) adalah teknik lanjutan dalam mengerjakan sebuah fungsi. _Currying_ tidak hanya digunakan di JavaScript tetapi dalam bahasa lain juga.\n\n_Currying_ adalah transformasi fungsi yang mengubah fungsi yang dipanggil sebagai `f(a, b, c)` menjadi `f(a)(b)(c)`.\n\n_Currying_ tidak memanggil suatu fungsi melainkan hanya mengubahnya.\n\nMari kita lihat contoh terlebih dahulu untuk memahami apa yang akan kita bicarakan lalu kemudian mempraktikkannya.\n\nKita akan membuat fungsi pembantu `curry(f)` yang melakukan _currying_ untuk dua argumen `f`. Dengan kata lain, `curry(f)` untuk dua argumen `f(a, b)` diubah menjadi fungsi yang dijalankan sebagai `f(a)(b)`:\n\n```js run\n*!*\nfunction curry(f) { // curry(f) melakukan currying\n  return function(a) {\n    return function(b) {\n      return f(a, b);\n    };\n  };\n}\n*/!*\n\n// penggunan\nfunction sum(a, b) {\n  return a + b;\n}\n\nlet curriedSum = curry(sum);\n\nalert( curriedSum(1)(2) ); // 3\n```\n\nSeperti yang Anda lihat, implementasinya cukup mudah: hanya membutuhkan dua pembungkus.\n\n- Hasil dari `curry(func)` adalah pembungkus `function(a)`.\n- Ketika dipanggil `curriedSum(1)`, argumen disimpan di lingkungan leksikal, dan pembungkus baru dikembalikan `function(b)`.\n- Kemudian pembungkus ini dipanggil dengan `2` sebagai argumen, dan ini meneruskan panggilan ke fungsi `sum` yang asli.\n\nImplementasi currying yang lebih lanjut, seperti [_.curry](https://lodash.com/docs#curry) dari _library_ lodash, mengembalikan pembungkus yang memungkinkan fungsi dipanggil secara normal maupun parsial:\n\n```js run\nfunction sum(a, b) {\n  return a + b;\n}\n\nlet curriedSum = _.curry(sum); // menggunakan _.curry dari library lodash \n\nalert( curriedSum(1, 2) ); // 3, tetap bisa dijalankan secara normal\nalert( curriedSum(1)(2) ); // 3, secara parsial\n```\n\n## _Currying_? Untuk apa?\n\nUntuk memahami manfaatnya kita membutuhkan contoh implementasi di dunia nyata.\n\nSebaga contoh, kita memiliki fungsi pencatatan `log(date, importance, message)` yang memformat dan mengeluarkan informasi. Dalam proyek yang sebenarnya, fungsi seperti itu memiliki banyak fitur yang berguna seperti mengirim log melalui jaringan, disini kita akan menggunakan `alert`:\n\n```js\nfunction log(date, importance, message) {\n  alert(`[${date.getHours()}:${date.getMinutes()}] [${importance}] ${message}`);\n}\n```\n\nMari lakukan _currying_!\n\n```js\nlog = _.curry(log);\n```\n\nSetelah itu `log` berjalan normal:\n\n```js\nlog(new Date(), \"DEBUG\", \"some debug\"); // log(a, b, c)\n```\n\n...Tetapi juga bekerja dalam bentuk _currying_:\n\n```js\nlog(new Date())(\"DEBUG\")(\"some debug\"); // log(a)(b)(c)\n```\n\nSekarang kita dapat dengan mudah membuat fungsi untuk log saat ini:\n\n```js\n// logNow akan menjadi bagian dari log dengan argumen pertama tetap\nlet logNow = log(new Date());\n\n// gunakan\nlogNow(\"INFO\", \"pesan\"); // [HH:mm] INFO pesan\n```\n\nSekarang `logNow` adalah `log` dengan argumen pertama yang sudah ditentukan, dengan kata lain \"fungsi yang diterapkan sebagian\" atau singkatnya \"parsial\".\n\nKita bisa melanjutkannya dan membuat fungsi untuk log _debug_ saat ini:\n\n```js\nlet debugNow = logNow(\"DEBUG\");\n\ndebugNow(\"pesan\"); // [HH:mm] DEBUG pesan\n```\n\nJadi:\n1. Kita tidak kehilangan apapun setelah melakukan _currying_: `log` tetap bisa dipanggil secara normal.\n2. Kita dapat dengan mudah membuat fungsi parsial seperti \"log untuk hari ini\".\n\n## Implementasi _currying_ lanjutan\n\nJika Anda ingin mengetahui lebih detail, berikut implementasi _currying_ \"lanjutan\" untuk fungsi multi-argumen yang dapat kita gunakan di atas.\n\nItu cukup pendek:\n\n```js\nfunction curry(func) {\n\n  return function curried(...args) {\n    if (args.length >= func.length) {\n      return func.apply(this, args);\n    } else {\n      return function(...args2) {\n        return curried.apply(this, args.concat(args2));\n      }\n    }\n  };\n\n}\n```\n\nContoh penggunaan:\n\n```js\nfunction sum(a, b, c) {\n  return a + b + c;\n}\n\nlet curriedSum = curry(sum);\n\nalert( curriedSum(1, 2, 3) ); // 6, tetap bisa dijalankan secara normal\nalert( curriedSum(1)(2,3) ); // 6, currying argumen pertama\nalert( curriedSum(1)(2)(3) ); // 6, currying secara penuh\n```\n\nFungsi `curry` yang baru mungkin terlihat rumit, tetapi sebenarnya cukup mudah dipahami.\n\nHasil dari pemanggilan `curry(func)` adalah pembungkus `curried` yang terlihat seperti ini:\n\n```js\n// func adalah fungsi untuk di transformasi\nfunction curried(...args) {\n  if (args.length >= func.length) { // (1)\n    return func.apply(this, args);\n  } else {\n    return function(...args2) { // (2)\n      return curried.apply(this, args.concat(args2));\n    }\n  }\n};\n```\n\nKetika kita menjalankannya, ada dua cabang eksekusi `if`:\n\n1. Jika lewat `args` count adalah sama atau lebih dari fungsi asli memiliki definisi (` func.length`), maka cukup teruskan panggilan menggunakan `func.apply`.\n2. Jika tidak, dapatkan sebagian: kita belum memanggil `func`. Sebagai gantinya, pembungkus lain dikembalikan, yang akan menerapkan kembali `curried` yang memberikan argumen sebelumnya bersama dengan yang baru.\n\nKemudian, jika kita menyebutnya, sekali lagi, kita akan mendapatkan parsial baru (jika tidak cukup argumen) atau, akhirnya, hasilnya.\n\n```smart header=\"Fixed-length functions only\"\nCurrying membutuhkan fungsi untuk memiliki sejumlah argumen tetap.\n\nFungsi yang menggunakan sisa parameter, seperti `f(...args)`, tidak bisa di currying dengan cara ini.\n```\n\n```smart header=\"A little more than currying\"\nMenurut definisi, currying harus mengubah `sum(a, b, c)` menjadi `sum(a)(b)(c)`.\n\nNamun sebagian besar implementasi currying di JavaScript bersifat lanjutan, seperti yang dijelaskan: implementasi tersebut juga membuat fungsi dapat dipanggil dalam bentuk multi-argumen.\n```\n\n## Ringkasan\n\n*Currying* adalah transformasi yang membuat `f(a,b,c)` dapat dipanggil sebagai `f(a)(b)(c)`. Implementasi JavaScript biasanya membuat fungsi dapat dipanggil secara normal dan mengembalikan dalam bentuk parsial jika jumlah argumen tidak cukup.\n\n_Currying_ memungkinkan kita untuk mendapatkan sebuah bagian. Seperti yang kita lihat di contoh logging, setelah _currying_ tiga argumen dari fungsi universal `log(date, importance, message)` akan memberikan kita fungsi parsial ketika dipanggil dengan satu argumen (seperti `log(date)`) atau dua argumen (seperti `log(date, importance)`).  \n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/2-check-syntax/solution.md",
    "content": "**Error**!\n\nCoba ini:\n\n```js run\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}\n\n(user.go)() // error!\n```\n\nPesan error pada kebanyakan peramban tidak memberitahukan kita cukup petunjuk tentang hal apa yang salah.\n\n**Error muncul karena tidak adanya sebuah titik koma setelah `user = {...}`.**\n\nJavaScript tidak secara otomatis menyisipkan sebuah tanda titik koma setelah tanda kurung kurawa `(user.go)()`, jadi JavaScript membaca kode seperti ini:\n\n```js no-beautify\nlet user = { go:... }(user.go)()\n```\n\nLalu kita juga bisa melihat bahwa ekspresi gabungan semacam itu adalah sebuah panggilan objek `{ go: ... }` secara sintaks yang juga sebagai sebuah fungsi dengan argumen `(user.go)`. Dan hal tersebut juga terjadi pada baris yang sama dengan `let user`, jadi objek `user` belum didefinisikan, oleh karena itu terjadi error.\n\nJika kita menyisipkan tanda titik koma, semuanya akan baik-baik saja:\n\n```js run\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}*!*;*/!*\n\n(user.go)() // John\n```\n\nTolong ingat bahwa tanda kurung kurawa yang merangkap `(user.go)` tidak melakukan apapun di sini. Biasanya  Biasanya tanda kurung kurawa mengatur urutan operasi, tapi di sini tanda titik-lah (`.`) yang berjalan terlebih dulu, jadi tidak ada pengaruh apapun. Hanya tanda titik koma yang berpengaruh.\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/2-check-syntax/task.md",
    "content": "importance: 2\n\n---\n\n# Cek sintaks\n\nApa hasil dari kode berikut ini?\n\n\n```js no-beautify\nlet user = {\n  name: \"John\",\n  go: function() { alert(this.name) }\n}\n\n(user.go)()\n```\n\nP.S. Ada jebakannya :)\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/3-why-this/solution.md",
    "content": "\nIni dia penjelasannya.\n\n1. Itu adalah sebuah panggilan metode objek biasa.\n\n2. Sama halnya, tanda kurung kurawa tidak merubah urutan operasi di sini, lagi pula tanda titik lah yang pertama di urutan operasi.\n\n3. Di sini kita memiliki sebuah panggilan yang lebih kompleks lagi yakni `(expression).method()`. Pagnggilan tersebut bekerja sebagaimana jika panggilan itu dipisah menjadi dua baris kode:\n\n    ```js no-beautify\n    f = obj.go; // mengkalkulasi ekspresi\n    f();        // memanggil apa yang kita punya \n    ```\n\n    Di sini `f()` dieksekusi sebagai sebuah fungsi, tanpa `this`.\n\n4. Hal serupa pada panggilan `(3)`, di sebelah kiri tanda titik `.` kita memiliki sebuah ekspresi.\n\nUntuk menjelaskan perilaku panggilan `(3)` dan `(4)` kita perlu memanggil ulang, yang mana properti pengakses (tanda titik atau tanda kurung siku) mengembalikan sebuah nilai dari tipe referensi (*Reference Type*).  \n\nOperasi apapun kecuali sebuah panggilan metode (seperti penugasan `=` atau `||`) membuat membuat ekspresi tersebut menjadi sebuah nilai biasa, yang tidak membawa informasi yang memungkinkan untuk menentukan `this`.\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/3-why-this/task.md",
    "content": "importance: 3\n\n---\n\n# Jelaskan nilai dari \"this\"\n\nDalam kode di bawah ini kita bermaksud untuk memanggil metode `obj.go()` sebanyak 4 kali sekaligus.\n\nTapi panggilan `(1)` dan `(2)` bekerja berbeda dibanding dengan `(3)` dan `(4)`. Mengapa demikian?\n\n```js run no-beautify\nlet obj, method;\n\nobj = {\n  go: function() { alert(this); }\n};\n\nobj.go();               // (1) [objek Object]\n\n(obj.go)();             // (2) [objek Object]\n\n(method = obj.go)();    // (3) undefined\n\n(obj.go || obj.stop)(); // (4) undefined\n```\n\n"
  },
  {
    "path": "1-js/99-js-misc/04-reference-type/article.md",
    "content": "\n# Reference Type\n\n```warn header=\"In-depth language feature\"\nThis article covers an advanced topic, to understand certain edge-cases better.\n\nIt's not important. Many experienced developers live fine without knowing it. Read on if you want to know how things work under the hood.\n```\n\nA dynamically evaluated method call can lose `this`.\n\nFor instance:\n\n```js run\nlet user = {\n  name: \"John\",\n  hi() { alert(this.name); },\n  bye() { alert(\"Bye\"); }\n};\n\nuser.hi(); // works\n\n// now let's call user.hi or user.bye depending on the name\n*!*\n(user.name == \"John\" ? user.hi : user.bye)(); // Error!\n*/!*\n```\n\nOn the last line there is a conditional operator that chooses either `user.hi` or `user.bye`. In this case the result is `user.hi`.\n\nThen the method is immediately called with parentheses `()`. But it doesn't work correctly!\n\nAs you can see, the call results in an error, because the value of `\"this\"` inside the call becomes `undefined`.\n\nThis works (object dot method):\n```js\nuser.hi();\n```\n\nThis doesn't (evaluated method):\n```js\n(user.name == \"John\" ? user.hi : user.bye)(); // Error!\n```\n\nWhy? If we want to understand why it happens, let's get under the hood of how `obj.method()` call works.\n\n## Reference type explained\n\nLooking closely, we may notice two operations in `obj.method()` statement:\n\n1. First, the dot `'.'` retrieves the property `obj.method`.\n2. Then parentheses `()` execute it.\n\nSo, how does the information about `this` get passed from the first part to the second one?\n\nIf we put these operations on separate lines, then `this` will be lost for sure:\n\n```js run\nlet user = {\n  name: \"John\",\n  hi() { alert(this.name); }\n}\n\n*!*\n// split getting and calling the method in two lines\nlet hi = user.hi;\nhi(); // Error, because this is undefined\n*/!*\n```\n\nHere `hi = user.hi` puts the function into the variable, and then on the last line it is completely standalone, and so there's no `this`.\n\n**To make `user.hi()` calls work, JavaScript uses a trick -- the dot `'.'` returns not a function, but a value of the special [Reference Type](https://tc39.github.io/ecma262/#sec-reference-specification-type).**\n\nThe Reference Type is a \"specification type\". We can't explicitly use it, but it is used internally by the language.\n\nThe value of Reference Type is a three-value combination `(base, name, strict)`, where:\n\n- `base` is the object.\n- `name` is the property name.\n- `strict` is true if `use strict` is in effect.\n\nThe result of a property access `user.hi` is not a function, but a value of Reference Type. For `user.hi` in strict mode it is:\n\n```js\n// Reference Type value\n(user, \"hi\", true)\n```\n\nWhen parentheses `()` are called on the Reference Type, they receive the full information about the object and its method, and can set the right `this` (`=user` in this case).\n\nReference type is a special \"intermediary\" internal type, with the purpose to pass information from dot `.` to calling parentheses `()`.\n\nAny other operation like assignment `hi = user.hi` discards the reference type as a whole, takes the value of `user.hi` (a function) and passes it on. So any further operation \"loses\" `this`.\n\nSo, as the result, the value of `this` is only passed the right way if the function is called directly using a dot `obj.method()` or square brackets `obj['method']()` syntax (they do the same here). There are various ways to solve this problem such as [func.bind()](/bind#solution-2-bind).\n\n## Summary\n\nReference Type is an internal type of the language.\n\nReading a property, such as with dot `.` in `obj.method()` returns not exactly the property value, but a special \"reference type\" value that stores both the property value and the object it was taken from.\n\nThat's for the subsequent method call `()` to get the object and set `this` to it.\n\nFor all other operations, the reference type automatically becomes the property value (a function in our case).\n\nThe whole mechanics is hidden from our eyes. It only matters in subtle cases, such as when a method is obtained dynamically from the object, using an expression.\n"
  },
  {
    "path": "1-js/99-js-misc/05-bigint/article.md",
    "content": "# BigInt\n\n[recent caniuse=\"bigint\"]\n\n`BigInt` is a special numeric type that provides support for integers of arbitrary length.\n\nA bigint is created by appending `n` to the end of an integer literal or by calling the function `BigInt` that creates bigints from strings, numbers etc.\n\n```js\nconst bigint = 1234567890123456789012345678901234567890n;\n\nconst sameBigint = BigInt(\"1234567890123456789012345678901234567890\");\n\nconst bigintFromNumber = BigInt(10); // same as 10n\n```\n\n## Math operators\n\n`BigInt` can mostly be used like a regular number, for example:\n\n```js run\nalert(1n + 2n); // 3\n\nalert(5n / 2n); // 2\n```\n\nPlease note: the division `5/2` returns the result rounded towards zero, without the decimal part. All operations on bigints return bigints.\n\nWe can't mix bigints and regular numbers:\n\n```js run\nalert(1n + 2); // Error: Cannot mix BigInt and other types\n```\n\nWe should explicitly convert them if needed: using either `BigInt()` or `Number()`, like this:\n\n```js run\nlet bigint = 1n;\nlet number = 2;\n\n// number to bigint\nalert(bigint + BigInt(number)); // 3\n\n// bigint to number\nalert(Number(bigint) + number); // 3\n```\n\nThe conversion operations are always silent, never give errors, but if the bigint is too huge and won't fit the number type, then extra bits will be cut off, so we should be careful doing such conversion.\n\n````smart header=\"The unary plus is not supported on bigints\"\nThe unary plus operator `+value` is a well-known way to convert `value` to a number.\n\nIn order to avoid confusion, it's not supported on bigints:\n```js run\nlet bigint = 1n;\n\nalert( +bigint ); // error\n```\nSo we should use `Number()` to convert a bigint to a number.\n````\n\n## Comparisons\n\nComparisons, such as `<`, `>` work with bigints and numbers just fine:\n\n```js run\nalert( 2n > 1n ); // true\n\nalert( 2n > 1 ); // true\n```\n\nPlease note though, as numbers and bigints belong to different types, they can be equal `==`, but not strictly equal `===`:\n\n```js run\nalert( 1 == 1n ); // true\n\nalert( 1 === 1n ); // false\n```\n\n## Boolean operations\n\nWhen inside `if` or other boolean operations, bigints behave like numbers.\n\nFor instance, in `if`, bigint `0n` is falsy, other values are truthy:\n\n```js run\nif (0n) {\n  // never executes\n}\n```\n\nBoolean operators, such as `||`, `&&` and others also work with bigints similar to numbers:\n\n```js run\nalert( 1n || 2 ); // 1 (1n is considered truthy)\n\nalert( 0n || 2 ); // 2 (0n is considered falsy)\n```\n\n## Polyfills\n\nPolyfilling bigints is tricky. The reason is that many JavaScript operators, such as `+`, `-` and so on behave differently with bigints compared to regular numbers.\n\nFor example, division of bigints always returns a bigint (rounded if necessary).\n\nTo emulate such behavior, a polyfill would need to analyze the code and replace all such operators with its functions. But doing so is cumbersome and would cost a lot of performance.\n\nSo, there's no well-known good polyfill.\n\nAlthough, the other way around is proposed by the developers of [JSBI](https://github.com/GoogleChromeLabs/jsbi) library.\n\nThis library implements big numbers using its own methods. We can use them instead of native bigints:\n\n| Operation | native `BigInt` | JSBI |\n|-----------|-----------------|------|\n| Creation from Number | `a = BigInt(789)` | `a = JSBI.BigInt(789)` |\n| Addition | `c = a + b` | `c = JSBI.add(a, b)` |\n| Subtraction\t| `c = a - b` | `c = JSBI.subtract(a, b)` |\n| ... | ... | ... |\n\n...And then use the polyfill (Babel plugin) to convert JSBI calls to native bigints for those browsers that support them.\n\nIn other words, this approach suggests that we write code in JSBI instead of native bigints. But JSBI works with numbers as with bigints internally, emulates them closely following the specification, so the code will be \"bigint-ready\".\n\nWe can use such JSBI code \"as is\" for engines that don't support bigints and for those that do support - the polyfill will convert the calls to native bigints.\n\n## References\n\n- [MDN docs on BigInt](mdn:/JavaScript/Reference/Global_Objects/BigInt).\n- [Specification](https://tc39.es/ecma262/#sec-bigint-objects).\n"
  },
  {
    "path": "1-js/99-js-misc/index.md",
    "content": "\n# Miscellaneous\n"
  },
  {
    "path": "1-js/index.md",
    "content": "# Bahasa JavaScript\n\nDi sini kita belajar JavaScript, mulai dari nol dan lanjut terus hingga konsep yang mutakhir macam OOP.\n\nKonsentrasi kita di sini tertuju ke bahasanya itu sendiri, dengan catatan lingkungan tertentu se-minimum mungkin."
  },
  {
    "path": "2-ui/1-document/01-browser-environment/article.md",
    "content": "\n# Browser environment, specs\nBahasa JavaScript awal mulanya dibuat untuk web browser. Sejak saat itu terus berevolusi dan menjadi sebuah bahasa dengan banyak pengguna dan platform.\n\nSebuah platform bisa menjadi browser, atau sebuah web-server ataupun *host* yang lain, sampai sebuah mesin kopi yang \"cerdas\", jika itu bisa menjalankan JavaScript. Masing-masing darinya menyediakan fungsionalitas platform yang spesifik. Spesifik JavaScript menyebut itu sebagai sebuah *host environment*.\n\nSebuah host environment menyediakan objek-objek tersendiri dan fungsi-fungsi tambahan ke pusat bahasa. Web browser memberikan sebuah sarana untuk mengontrol halaman-halaman web.\n\nBerikut adalah sebuah pandangan luas tentang apa yang kita punya  ketika JavaScript berjalan di sebuah web browser:\n\n![](windowObjects.svg)\n\nAda sebuah \"root\" objek yang dinamakan `window`. Mempunyai 2 peranan:\n\n1. Pertama, ini adalah sebuah global objek untuk kode JavaScript, dideskripsikan di bab <info:global-object>.\n2. Kedua, ini mempresentasikan dari \"browser window\" dan menyediakan metode-metode untuk mengontrolnya.\n\nMisalnya, disini kita akan menggunakannya sebagai sebuah global objek:\n\n```js berjalan\nfunction sayHi() {\n  alert(\"Hello\");\n}\n\n// fungsi-fungsi global adalah metode objek global:\n\nwindow.sayHi();\n```\n\nDan sekarang kita menggunakannya sebagai jendela browser, untuk melihat tinggi jendela:\n\n```js berjalan\nalert(window.innerHeight); // tinggi jendela bagian dalam\n```\n\nBerikut lebih banyak metode dan properti window-specific, kita akan membahasnya nanti.\n\n## DOM (Document Object Model)\nDocument Object Model, atau disingkat DOM, mempresentasikan semua konten halaman sebagai objek yang bisa dimodifikasi.\n\nObjek `document` adalah \"pintu masuk\" utama dari halaman. Kita bisa mengubahnya atau membuat apapun untuk dipakai halaman tersebut.\n\nSebagai contoh:\n```js run\n// ubah warna latar belakang menjadi merah\ndocument.body.style.background = \"red\";\n\n// ubah kembali setelah 1 detik\nsetTimeout(() => document.body.style.background = \"\", 1000);\n```\n\nDisini kita menggunakan `document.body.style`, tapi ada lebih banyak, banyak lagi. Properti dan metode-metode dideskripsikan di spesifikasi: [DOM Living Standard](https://dom.spec.whatwg.org).\n\n```smart header=\"DOM is not only for browsers\"\nSpesifikasi DOM menjelaskan struktur dari sebuah dokumen dan penyedia objek untuk memanipulasinya. Ada instrumen dari non-browser yang menggunakan DOM juga.\n\nSebagai contoh, skrip server-side yang mengunduh halaman-halaman HTML dan memprosesnya juga menggunakan DOM. Mereka mungkin hanya mendukung sebagian dari spesifikasi tersebut.\n```\n\n```smart header=\"CSSOM for styling\"\nAda juga spesifikasi terpisah, [CSS Object Model (CSSOM)](https://www.w3.org/TR/cssom-1/) untuk aturan dan stylesheets CSS, yang menjelaskan bagaimana mereka di representasilan sebagai objek, dan bagaimana mereka membaca dan menulisnya.\n\nCSSOM digunakan bersamaan dengan DOM ketika kita memodifikasi style aturan untuk dokumen. Di prakteknya sekalipun, CSSOM jarang dibutuhkan, karena kita jarang untuk memodifikasi aturan CSS dari JavaScript (biasanya kita hanya menambah/menghapus class dari CSS, tidak memodifikasi aturan dari CSS-nya), tapi itu juga memungkinkan.\n```\n\n## BOM (Browser Object Model)\n\nBrowser Object Model (BOM) mempresentasikan tambahan dari objek-objek yang disediakan oleh browser (browser environment) untuk dikerjakan oleh apapun kecuali oleh dokumen.\n\nSebagai contoh:\n\n- Objek [navigator](mdn:api/Window/navigator) menyediakan informasi di latar belakang tentang browser dan operasi sistem. Disitu banyak sekali properti, tapi ada 2 yang sudah banyak diketahui yaitu: `navigator.userAgent` -- tentang browser sekarang, dan `navigator.platform` -- tentang platform (yang bisa membantu untuk membedakan antara Windows/Linux/Mac dan sebagainya).\n- Objek [location](mdn:api/Window/location) memungkinkan kita untuk membaca URL sekarang yang akan mengarahkan browser ke satu yang baru.\n\nBerikut adalah bagaimana kita bisa menggunakan objek `location`:\n\n```js run\nalert(location.href); // tampilkan URL sekarang\nif (confirm(\"Pergi ke Wikipedia?\")) {\n  location.href = \"https://wikipedia.org\"; // mengarahkan browser ke URL yang lain\n}\n```\n\nFungsi `alert/confirm/prompt` juga bagian dari BOM: mereka tidak berhubungan secara langsung dengan dokumen, tapi mempresentasikan metode komunikasi dengan pengguna pada browser asli.\n\n```smart header=\"Specifications\"\nBOM adalah bagian dari [spesifikasi HTML](https://html.spec.whatwg.org) pada umumnya.\n\nYa, yang kamu dengar benar. Spesifikasi HTML pada <https://html.spec.whatwg.org> bukanlah satu-satunya tentang \"bahasa HTML\" (tags, attributes), tapi juga mencakup banyak objek, metode-metode dan spesifikasi-browser ekstensi DOM. Itu adalah \"HTML dalam istilah yang luas\". Juga, beberapa bagian-bagian punya tambahan spesifikasi yang terdaftar di <https://spec.whatwg.org>.\n```\n\n## Ringkasan\n\nBerbicara tentang standard, kita mempunyai:\n\nSpesifikasi DOM\n: Mendeskripsikan struktur dokumen, manipulasi dan events, lihat <https://dom.spec.whatwg.org>.\n\nSpesifikasi CSSOM\n: Mendeskripsikan stylesheets dan aturan gaya, manipulasi dengannya dan perbandingannya dengan dokumen, lihat <https://www.w3.org/TR/cssom-1/>.\n\nSpesifikasi HTML\n: Menjelaskan bahasa HTML (contoh. tags) dan juga BOM (browser object model) -- macam-macam fungsi browser: `setTimeout`, `alert`, `location` dan banyak lagi, lihat <https://html.spec.whatwg.org>. Dengan itu bisa mengambil spesifikasi DOM dan meluaskannya dengan banyak properti dan metode.\n\nSelain itu, beberapa class telah dideskripsikan terpisah di <https://spec.whatwg.org/>.\n\nMohon catat tautan ini, karena ada begitu banyak hal untuk dipelajari dan mustahil untuk mencakup dan  mengingat semuanya.\n\nKetika  anda ingin untuk membaca tentang sebua properti atau sebuah metode, Mozilla manual di <https://developer.mozilla.org/en-US/search> juga sebuah bahan yang bagus, tapi kesesuaian spesifikasi mungkin lebih baik: ada yang lebih kompleks dan bacaan yang panjang, tapi akan membuat dasar pengetahuan anda bersuara dan lengkap.\n\nUntuk menemukan sesuatu, akan semakin nyaman memakai pencarian internet \"WHATWG [term]\" atau \"MDN [term]\", contoh <https://google.com?q=whatwg+localstorage>, <https://google.com?q=mdn+localstorage>.\n\nSekarang kita akan turun untuk mempelajari DOM, karena dokumen memiliki peranan pusat dalam UI.\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/article.md",
    "content": "libs:\n  - d3\n  - domtree\n\n---\n\n# DOM tree\n\nTulang punggung dari dokumen HTML adalah tag.\n\nBerdasarkan Document Object Model (DOM), setiap tag HTML merupakan sebuah objek. Tag berlapis adalah \"anak\" dari tag yang melampirkan. Teks di dalam sebuah tag merupakan sebuah objek juga.\n\nSemua objek ini dapat diakses menggunakan JavaScript, dan kita bisa menggunakannya untuk memodifikasi halaman.\n\nMisalnya, `document.body` merupakan objek yang merepresentasikan tag `<body>`.\n\nMenjalankan kode ini akan membuat `<body>` menjadi merah selama 3 detik.\n\n```js run\ndocument.body.style.background = 'red'; // buat background menjadi merah\n\nsetTimeout(() => document.body.style.background = '', 3000); // kembalikan seperti semula\n```\n\nDisini kita menggunakan `style.background` untuk mengubah warna background `document.body`, tetapi ada banyak properti lain, seperti:\n\n- `innerHTML` -- Konten-konten HTML dari node.\n- `offsetWidth` -- lebar node (dalam piksel)\n- ...dan seterusnya.\n\nKita akan segera mempelajari lebih banyak cara untuk memanipulasi DOM, tetapi pertama-tama kita perlu mengetahui tentang strukturnya.\n\n## Contoh dari DOM\n\nMari kita mulai dengan dokumen sederhana berikut:\n\n```html run no-beautify\n<!DOCTYPE HTML>\n<html>\n<head>\n  <title>About elk</title>\n</head>\n<body>\n  The truth about elk.\n</body>\n</html>\n```\n\nDOM menggambarkan HTML seperti struktur pohon pada tag. Begini tampilannya:\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node1 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  \"},{\"name\":\"TITLE\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"About elk\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\"},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  The truth about elk.\\n\\n\\n\"}]}]}\n\ndrawHtmlTree(node1, 'div.domtree', 690, 320);\n</script>\n\n```online\nPada gambar di atas, Anda dapat mengklik node elemen dan anaknya akan membuka/menutup.\n```\n\nSetiap node pohon merupakan sebuah objek.\n\nTag-tag merupakan *node elemen* (atau hanya elemen) dan membentuk struktur pohon: `<html>` merupakan root, kemudian `<head>` dan `<body>` adalah anak-anaknya, dll.\n\nTeks di dalam elemen-elemen membentuk *node teks*, dilabeli sebagai `#text`. Sebuah node teks hanya berisi string. Ia mungkin tidak memiliki anak dan selalu menjadi daun pohon.\n\nMisalnya, tag `<title>` memiliki teks `\"About elk\"`\n\nHarap perhatikan karakter khusus dalam node teks:\n\n- baris baru: `↵` (di dalam JavaScript seperti `\\n`)\n- spasi: `␣`\n\nSpasi dan baris baru adalah karakter yang benar-benar valid, seperti huruf-huruf dan angka-angka. Mereka membentuk node teks dan menjadi bagian dari DOM. Jadi, misalnya, pada contoh di atas, tag `<head>` berisi beberapa spasi sebelum `<title>`, dan teks tersebut menjadi node `#teks` (ini berisi baris baru dan beberapa spasi).\n\nHanya ada dua pengecualian top-level:\n1. Spasi dan baris baru sebelum `<head>` diabaikan karena alasan historis.\n2. Jika kita meletakkan sesuatu setelah `</body>`, maka secara otomatis dipindahkan ke dalam `body`, di bagian bawah, karena spesifikasi HTML mengharuskan semua konten harus berada di dalam `<body>`. Jadi tidak boleh ada spasi setelah `</body>`.\n\nDalam kasus lain semuanya mudah -- Jika ada spasi-spasi (seperti karakter lainnya) di dalam dokumen, maka mereka menjadi node teks di DOM tersebut, dan jika kita menghapusnya, maka akan hilang.\n\nBerikut tidak ada node teks khusus spasi:\n\n```html no-beautify\n<!DOCTYPE HTML>\n<html><head><title>About elk</title></head><body>The truth about elk.</body></html>\n```\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node2 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[{\"name\":\"TITLE\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"About elk\"}]}]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"The truth about elk.\"}]}]}\n\ndrawHtmlTree(node2, 'div.domtree', 690, 210);\n</script>\n\n```smart header=\"Spasi di awal/akhir string dan node teks khusus spasi biasanya disembunyikan di alat\"\nAlat browser (akan segera dibahas) yang bekerja dengan DOM biasanya tidak menampilkan spasi di awal/akhir teks dan node teks kosong (jeda baris) di antara tag.\n\nAlat pengembang menghemat ruang layar dengan cara ini.\n\nPada gambar DOM lebih lanjut, kita terkadang mengabaikannya saat mereka tidak relevan. Spasi seperti itu biasanya tidak mempengaruhi bagaimana dokumen ditampilkan.\n```\n\n## Autocorrection\n\nJika browser menemukan HTML yang salah format, browser akan memperbaikinya secara otomatis saat membuat DOM.\n\nMisalnya, tag yang paling atas selalu `<html>`. Bahkan jika itu tidak ada di dalam dokumen, ia akan ada ada sendiri di dalam DOM, karena browser tersebut akan membuatnya. Hal yang sama berlaku untuk `<body>`.\n\nContoh, jika file HTML kata tunggal `\"Hello\"`, browser akan membungkusnya ke dalam `<html>` dan `<body>`, dan menambahkan `<head>` yang diperlukan, dan DOM akan menjadi seperti ini:\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node3 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Hello\"}]}]}\n\ndrawHtmlTree(node3, 'div.domtree', 690, 150);\n</script>\n\nSelagi sedang menghasilkan DOM, browser secara otomatis memproses kesalahan-kesalahan di dalam dokumen, tag penutup, dan sebagainya.\n\ndokumen dengan tag yang tidak ditutup:\n\n```html no-beautify\n<p>Hello\n<li>Mom\n<li>and\n<li>Dad\n```\n\n...Akan menjadi DOM normal saat browser membaca tag dan memulihkan bagian yang hilang:\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node4 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"P\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Hello\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Mom\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"and\"}]},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"Dad\"}]}]}]}\n\ndrawHtmlTree(node4, 'div.domtree', 690, 360);\n</script>\n\n````warn header=\"Tabel selalu memiliki `<tbody>`\"\n“Kasus khusus” yang menarik adalah tabel. Berdasarkan spesifikasi DOM, mereka harus memiliki tag <tbody>, tetapi teks HTML dapat menghilangkannya. Kemudian browser membuat <tbody> di DOM secara otomatis.\n\nUntuk HTML:\n\n```html no-beautify\n<table id=\"table\"><tr><td>1</td></tr></table>\n```\n\nStruktur DOM akan seperti ini:\n<div class=\"domtree\"></div>\n\n<script>\nlet node5 = {\"name\":\"TABLE\",\"nodeType\":1,\"children\":[{\"name\":\"TBODY\",\"nodeType\":1,\"children\":[{\"name\":\"TR\",\"nodeType\":1,\"children\":[{\"name\":\"TD\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"1\"}]}]}]}]};\n\ndrawHtmlTree(node5,  'div.domtree', 600, 200);\n</script>\n\nAnda lihat? `<tbody>` muncul entah dari mana. Kita harus mengingat ini saat bekerja dengan tabel untuk menghindari kejutan.\n\n\n## Jenis-jenis node lain\n\nAda beberapa jenis-jenis node lain selain node elemen dan teks.\n\nContoh, komentar:\n\n```html\n<!DOCTYPE HTML>\n<html>\n<body>\n  The truth about elk.\n  <ol>\n    <li>An elk is a smart</li>\n*!*\n    <!-- comment -->\n*/!*\n    <li>...and cunning animal!</li>\n  </ol>\n</body>\n</html>\n```\n\n<div class=\"domtree\"></div>\n\n<script>\nlet node6 = {\"name\":\"HTML\",\"nodeType\":1,\"children\":[{\"name\":\"HEAD\",\"nodeType\":1,\"children\":[]},{\"name\":\"BODY\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  The truth about elk.\\n  \"},{\"name\":\"OL\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"An elk is a smart\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"#comment\",\"nodeType\":8,\"content\":\"comment\"},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n    \"},{\"name\":\"LI\",\"nodeType\":1,\"children\":[{\"name\":\"#text\",\"nodeType\":3,\"content\":\"...and cunning animal!\"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n  \"}]},{\"name\":\"#text\",\"nodeType\":3,\"content\":\"\\n\\n\\n\"}]}]};\n\ndrawHtmlTree(node6, 'div.domtree', 690, 500);\n</script>\n\nKita bisa lihat disini sebuah jenis node pohon baru -- *comment node*, yang dilabeli sebagai `#comment`, diantar dua node teks.\n\nKita mungkin berpikir -- kenapa komentar ditambahkan ke DOM? komentar tersebut tidak mempengaruhi representasi visual dengan cara apa pun. Tapi ada sebuah aturan -- jika sesuatu ada di dalam HTML, maka ia juga harus di dalam pohon DOM.\n\n**Semuanya di dalam HTML, bahkan komentar, akan menjadi bagian dari DOM.**\n\nBahkan direktif `<!DOCTYPE ...>` di awal HTML juga merupakan node DOM. Letaknya di pohon DOM tepat sebelum `<html>`. Kita tidak akan menyentuh node itu, Kita bahkan tidak menggambarnya pada diagram karena alasan itu, tetapi node itu ada.\n\nObjek `document` yang mewakili seluruh dokumen, secara formal, juga merupakan node DOM.\n\nAda [12 jenis-jenis node](https://dom.spec.whatwg.org/#node). Dalam praktiknya kita biasanya bekerja dengan 4 di antaranya:\n\n1. `document` -- \"titik masuk\" ke DOM.\n2. node elemen -- tag-tag HTML, blok bangunan pohon.\n3. node teks -- berisi teks.\n4. komentar -- terkadang kita meletakkan informasi disini, ia tidak akan ditampilkan, tetapi JS bisa membacanya dari DOM.\n\n## Melihatnya untuk kita sendiri\n\nUntuk melihat struktur DOM secara real-time, coba [Live DOM Viewer](http://software.hixie.ch/utilities/js/live-dom-viewer/). Cukup ketikkan dokumen, dan itu akan muncul sebagai DOM dalam sekejap.\n\nCara lain untuk menjelajahi DOM gunakan alat pengembang browser. Sebenarnya itulah yang kita gunakan saat mengembangkan.\n\nUntuk melakukannya, buka halaman web [elk.html](elk.html), aktifkan alat pengembang browser dan beralih ke tab Elemen.\n\nSeharusnya tampil seperti ini:\n\n![](elk.svg)\n\nAnda bisa melihat DOM tersebut, klik pada elemen-elemen, melihat detailnya, dan sebagainya.\n\nHarap perhatikan bahwa struktur DOM di alat pengembang disederhanakan. Node teks ditampilkan hanya sebagai teks. Dan tidak ada samak sekali node teks \"kosong\" (hanya spasi). Tidak apa-apa, karena sebagian besar waktu kita berkepentingan pada node elemen.\n\nMengklik tombol <span class=\"devtools\" style=\"background-position:-328px -124px\"></span> di pojok kiri atas memungkinkan kita memilih node dari halaman web menggunakan mouse (atau perangkat penunjuk lain) dan \"memeriksanya\" (gulir ke sana di tab Elemen). Ini berfungsi dengan baik ketika kita memiliki halaman HTML besar (dan DOM besar yang sesuai) dan ingin melihat tempat elemen tertentu di dalamnya.\n\nCara lain untuk melakukannya adalah dengan mengklik kanan pada halaman web dan memilih \"Inspect\" di menu konteks.\n\n![](inspect.svg)\n\nDi bagian kanan alat ada subtabs berikut:\n- **Styles** -- kita bisa melihat CSS diterapkan ke elemen saat ini aturan demi aturan, termasuk aturan bawaan (abu-abu). Hampir semuanya dapat diedit di tempat, termasuk dimensi/margin/padding kotak di bawah ini.\n- **Computed** -- untuk melihat CSS diterapkan ke elemen berdasarkan properti: untuk setiap properti kita dapat melihat aturan yang memberikannya (termasuk pewarisan CSS dan semacamnya).\n- **Event Listeners** -- untuk melihat event listener yang dilampirkan ke elemen DOM (kita akan membahasnya di bagian selanjutnya dari tutorial).\n- ...dan seterusnya.\n\nCara terbaik untuk mempelajarinya adalah dengan mengklik. Sebagian besar nilai dapat diedit di tempat.\n\n## Interaksi dengan konsol\n\nSaat kami mengerjakan DOM, kita juga mungkin ingin menerapkan JavaScript padanya. Seperti: mendapatkan node dan jalankan beberapa kode untuk memodifikasinya, Berikut beberapa tip untuk berpindah antara tab Elemen dan konsol.\n\nSebagai permulaan:\n\n1. Pilih `<li>` pertama di dalam tab Elements.\n2. Tekan `key:Esc` -- itu akan membuka konsol tepat di bawah tab Elements.\n\nSekarang elemen yang dipilih terakhir tersedia sebagai `$0`, yang dipilih sebelumnya adalah `$1` dll.\n\nKita bisa menjalankan perintah pada mereka. Misalnya , `$0.style.background = 'red'` membuat item list yang dipilih bewarna merah, seperti ini:\n\n![](domconsole0.svg)\n\nBegitulah cara mendapatkan node dari Elements di Console.\n\nAda juga jalan kembali. Jika ada variabel yang mereferensikan node DOM, maka kita dapat menggunakan perintah `inspect (node)` di Console untuk melihatnya di panel Elements.\n\nAtau kita bisa mengeluarkan simpul DOM di konsol dan menjelajahi \"di tempat\", seperti `document.body` di bawah ini:\n\n![](domconsole1.svg)\n\nItu tentu saja untuk tujuan debugging. Dari bab selanjutnya kita akan mengakses dan memodifikasi DOM menggunakan JavaScript.\n\nAlat pengembang browser sangat membantu dalam pengembangan: kita dapat menjelajahi DOM, mencoba berbagai hal dan melihat apa yang salah.\n\n## Summary\n\nDokumen HTML/XML direpresentasikan di dalam browser sebagai pohon DOM.\n\n- Tag menjadi node elemen dan membentuk struktur.\n- Teks menjadi node teks.\n- ...dll, semuanya di dalam HTML mempunyai tempatnya di dalam DOM, bahkan komentar.\n\nKita dapat menggunakan alat pengembang untuk memeriksa DOM dan memodifikasinya secara manual.\n\nDi sini kami membahas dasar-dasar, tindakan yang paling sering digunakan dan penting untuk memulai. Ada dokumentasi lengkap tentang Alat Pengembang Chrome di <https://developers.google.com/web/tools/chrome-devtools>. Cara terbaik untuk mempelajari alat ini adalah dengan mengklik di sana-sini, membaca menu: sebagian besar opsi sudah jelas. Nanti, jika Anda mengenal mereka secara umum, bacalah dokumennya dan pelajari sisanya.\n\nNode DOM memiliki properti dan method yang memungkinkan kita untuk melakukan perjalanan di antara mereka, memodifikasinya, memindahkan halaman, dan banyak lagi. Kami akan membahasnya di bab berikutnya.\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/elk.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  The truth about elk.\n  <ol>\n    <li>An elk is a smart</li>\n    <!-- comment -->\n    <li>...and cunning animal!</li>\n  </ol>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/02-dom-nodes/head.html",
    "content": "<style>\nspan.devtools {\n  display: inline-block;\n  background-image: url(/article/dom-nodes/toolbarButtonGlyphs.svg);\n  height:16px;\n  width:16px;\n}\n</style>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/1-dom-children/solution.md",
    "content": "There are many ways, for instance:\n\n\nThe `<div>` DOM node:\n\n```js\ndocument.body.firstElementChild\n// or\ndocument.body.children[0]\n// or (the first node is space, so we take 2nd)\ndocument.body.childNodes[1]\n```\n\nThe `<ul>` DOM node:\n\n```js\ndocument.body.lastElementChild\n// or\ndocument.body.children[1]\n```\n\nThe second `<li>` (with Pete):\n\n```js\n// get <ul>, and then get its last element child\ndocument.body.lastElementChild.lastElementChild\n```\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/1-dom-children/task.md",
    "content": "importance: 5\n\n---\n\n# DOM children\n\nLook at this page:\n\n```html\n<html>\n<body>\n  <div>Users:</div>\n  <ul>\n    <li>John</li>\n    <li>Pete</li>\n  </ul>\n</body>\n</html>\n```\n\nFor each of the following, give at least one way of how to access them:\n- The `<div>` DOM node?\n- The `<ul>` DOM node?\n- The second `<li>` (with Pete)?\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/solution.md",
    "content": "1. Yes, true. The element `elem.lastChild` is always the last one, it has no `nextSibling`.\n2. No, wrong, because `elem.children[0]` is the first child *among elements*. But there may exist non-element nodes before it. So `previousSibling` may be a text node.\n\nPlease note: for both cases if there are no children, then there will be an error.\n\nIf there are no children, `elem.lastChild` is `null`, so we can't access `elem.lastChild.nextSibling`. And the collection `elem.children` is empty (like an empty array `[]`).\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/3-navigation-links-which-null/task.md",
    "content": "importance: 5\n\n---\n\n# The sibling question\n\nIf `elem` -- is an arbitrary DOM element node...\n\n- Is it true that `elem.lastChild.nextSibling` is always `null`?\n- Is it true that `elem.children[0].previousSibling` is always `null` ?\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.md",
    "content": "We'll be using `rows` and `cells` properties to access diagonal table cells.\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td {\n      border: 1px solid black;\n      padding: 3px 5px;\n    }\n  </style>\n</head>\n\n<body>\n  <table>\n    <tr>\n      <td>1:1</td>\n      <td>2:1</td>\n      <td>3:1</td>\n      <td>4:1</td>\n      <td>5:1</td>\n    </tr>\n    <tr>\n      <td>1:2</td>\n      <td>2:2</td>\n      <td>3:2</td>\n      <td>4:2</td>\n      <td>5:2</td>\n    </tr>\n    <tr>\n      <td>1:3</td>\n      <td>2:3</td>\n      <td>3:3</td>\n      <td>4:3</td>\n      <td>5:3</td>\n    </tr>\n    <tr>\n      <td>1:4</td>\n      <td>2:4</td>\n      <td>3:4</td>\n      <td>4:4</td>\n      <td>5:4</td>\n    </tr>\n    <tr>\n      <td>1:5</td>\n      <td>2:5</td>\n      <td>3:5</td>\n      <td>4:5</td>\n      <td>5:5</td>\n    </tr>\n  </table>\n  <script>\n    let table = document.body.firstElementChild;\n\n    for (let i = 0; i < table.rows.length; i++) {\n      let row = table.rows[i];\n      row.cells[i].style.backgroundColor = 'red';\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td {\n      border: 1px solid black;\n      padding: 3px 5px;\n    }\n  </style>\n</head>\n\n<body>\n  <table>\n    <tr>\n      <td>1:1</td>\n      <td>2:1</td>\n      <td>3:1</td>\n      <td>4:1</td>\n      <td>5:1</td>\n    </tr>\n    <tr>\n      <td>1:2</td>\n      <td>2:2</td>\n      <td>3:2</td>\n      <td>4:2</td>\n      <td>5:2</td>\n    </tr>\n    <tr>\n      <td>1:3</td>\n      <td>2:3</td>\n      <td>3:3</td>\n      <td>4:3</td>\n      <td>5:3</td>\n    </tr>\n    <tr>\n      <td>1:4</td>\n      <td>2:4</td>\n      <td>3:4</td>\n      <td>4:4</td>\n      <td>5:4</td>\n    </tr>\n    <tr>\n      <td>1:5</td>\n      <td>2:5</td>\n      <td>3:5</td>\n      <td>4:5</td>\n      <td>5:5</td>\n    </tr>\n  </table>\n  <script>\n    let table = document.body.firstElementChild;\n\n    // your code\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/4-select-diagonal-cells/task.md",
    "content": "importance: 5\n\n---\n\n# Select all diagonal cells\n\nWrite the code to paint all diagonal table cells in red.\n\nYou'll need to get all diagonal `<td>` from the `<table>` and paint them using the code:\n\n```js\n// td should be the reference to the table cell\ntd.style.backgroundColor = 'red';\n```\n\nThe result should be:\n\n[iframe src=\"solution\" height=180]\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/article.md",
    "content": "libs:\n\n- d3\n- domtree\n\n---\n\n# Menelusuri DOM\n\nDOM memungkinkan kita untuk melakukan apapun dengan elemen-elemen dan isinya, tetapi pertama-tama kita perlu mencapai objek DOM yang sesuai.\n\nSemua operasi pada DOM dimulai dengan objek `document`. Itulah \"titik masuk\" utama ke DOM. Dari objek tersebut, kita dapat mengakses setiap node (simpul).\n\nBerikut adalah gambar tautan yang memungkinkan untuk berpindah antara simpul-simpul DOM):\n\n![](dom-links.svg)\n\nMari kita bahas lebih detail.\n\n## Di Puncak: documentElement dan body\n\nSimpul paling atas dari struktur pohon tersedia langsung sebagai properti dari `document`:\n\n`<html>` = `document.documentElement` : Simpul dokumen paling atas adalah `document.documentElement`. Itu adalah simpul DOM dari tag `<html>`.\n\n`<body>` = `document.body` : Simpul DOM lain yang sering digunakan adalah elemen `<body>` -- `document.body`.\n\n`<head>` = `document.head` : Tag `<head>` tersedia sebagai `document.head`.\n\n> **PERINGATAN: Tapi, ada pengecualian: `document.body` bisa saja `null`.**\n>\n> Sebuah skrip tidak dapat mengakses elemen yang belum ada pada saat dijalankan.\n>\n> Khususnya, jika sebuah skrip berada di dalam tag `<head>`, maka `document.body` tidak tersedia, karena browser (peramban) belum membacanya.\n>\n> Jadi, pada contoh di bawah ini, `alert` pertama akan menampilkan `null`:\n>\n> ```html run\n> <html>\n>   <head>\n>     <script>\n>       *!*\n>           alert( \"Dari HEAD: \" + document.body ); // null, belum ada <body>\n>       */!*\n>     </script>\n>   </head>\n>\n>   <body>\n>     <script>\n>       alert(\"Dari BODY: \" + document.body); // HTMLBodyElement, sekarang sudah ada\n>     </script>\n>   </body>\n> </html>\n> ```\n\n> **Dalam dunia DOM, `null` berarti \\\"Tidak ada yang eksis.\\\"**\n> Dalam DOM, nilai `null` berarti \"tidak ada\" atau \"tidak ada simpul tersebut\".\n\n## Anak-anak (Children): childNodes, firstChild, lastChild\n\nAda dua istilah yang akan kita gunakan mulai sekarang:\n\n- **Simpul anak (Child nodes) atau Anak-anak (Children)** -- elemen-elemen yang merupakan anak langsung. Dengan kata lain, mereka bersarang tepat di dalam elemen yang diberikan. Misalnya, `<head>` dan `<body>` merupakan anak-anak dari elemen `<html>`.\n- **Keturunan (Descendants)** -- semua elemen yang bersarang di dalam elemen yang diberikan, termasuk anak-anak mereka, cucu-cucu mereka, dan seterusnya.\n\nMisalnya, di sini `<body>` memiliki anak-anak berupa `<div>` dan `<ul>` (dan beberapa simpul teks kosong):\n\n```html run\n<html>\n  <body>\n    <div>Mulai</div>\n\n    <ul>\n      <li>\n        <b>Informasi</b>\n      </li>\n    </ul>\n  </body>\n</html>\n```\n\n... Dan keturunan (descendants) dari `<body>` tidak hanya anak langsung `<div>`, `<ul>`, tetapi juga elemen-elemen yang bersarang lebih dalam, seperti `<li>` (anak dari `<ul>`) dan `<b>` (anak dari `<li>`) -- seluruh sub-pohon.\n\n**Koleksi `childNodes` mencantumkan semua simpul anak, termasuk simpul teks.**\n\nContoh di bawah ini menampilkan anak-anak dari `document.body`:\n\n```html run\n<html>\n  <body>\n    <div>Mulai</div>\n\n    <ul>\n      <li>Informasi</li>\n    </ul>\n\n    <div>Akhir</div>\n\n    <script>\n      *!*\n          for (let i = 0; i < document.body.childNodes.length; i++) {\n            alert( document.body.childNodes[i] ); // Text, DIV, Text, UL, ..., SCRIPT\n          }\n      */!*\n    </script>\n    ...lebih banyak hal...\n  </body>\n</html>\n```\n\nHarap perhatikan detail menarik di sini. Jika kita menjalankan contoh di atas, elemen terakhir yang ditampilkan adalah `<script>`. Sebenarnya, dokumen memiliki \"lebih banyak hal\" di bawahnya, tetapi pada saat eksekusi skrip, browser belum membacanya, sehingga skrip tidak melihatnya.\n\n**Properti `firstChild` dan `lastChild` memberikan akses cepat ke anak pertama dan anak terakhir.**\n\nMereka hanyalah cara atau jalan pintas. Jika ada simpul anak, maka pernyataan berikut selalu benar:\n\n```js\nelem.childNodes[0] === elem.firstChild; // true (benar)\nelem.childNodes[elem.childNodes.length - 1] === elem.lastChild; // true (benar)\n```\n\nJuga, ada fungsi khusus `elem.hasChildNodes()` untuk memeriksa apakah ada atau tidaknya simpul anak.\n\n### Koleksi DOM\n\nSeperti yang kita lihat, `childNodes` terlihat seperti sebuah array. Tetapi sebenarnya bukanlah sebuah array, melainkan sebuah _koleksi_ -- sebuah objek yang mirip array dengan sifat iterasi khusus.\n\nAda dua konsekuensi penting:\n\n1. Kita dapat menggunakan `for..of` untuk mengulanginya:\n\n   ```js\n   for (let node of document.body.childNodes) {\n     alert(node); // menampilkan semua simpul dari koleksi\n   }\n   ```\n\n   Hal ini dikarenakan koleksi tersebut dapat diiterasi (menyediakan properti `Symbol.iterator`, seperti yang dibutuhkan).\n\n2. Method-method array tidak akan berfungsi, karena ini bukanlah sebuah array:\n\n   ```js run\n   alert(document.body.childNodes.filter); // undefined (tidak ada method filter!)\n   ```\n\n   Hal pertama itu bagus. Yang kedua bisa ditoleransi, karena kita dapat menggunakan `Array.from` untuk membuat sebuah array \"sejati\" dari koleksi tersebut, jika kita membutuhkan method-method array:\n\n   ```js run\n   alert(Array.from(document.body.childNodes).filter); // fungsi\n   ```\n\n> **PERINGATAN: Koleksi-koleksi DOM bersifat hanya-baca (read-only).**\n>\n> Koleksi-koleksi DOM, bahkan lebih -- _semua_ properti navigasi yang tercantum dalam bab ini bersifat hanya-baca (read-only).\n>\n> Kita tidak dapat menggantikan suatu child (anak) dengan yang lain dengan cara menetapkan `childNodes[i] = ...`.\n>\n> Untuk mengubah DOM, diperlukan method-method lain. Kita akan melihatnya dalam bab selanjutnya.\n\n> **PERINGATAN: Koleksi-koleksi DOM bersifat dinamis (live).**\n>\n> Hampir semua koleksi DOM dengan beberapa pengecualian _bersifat dinamis (live)_. Dengan kata lain, mereka mencerminkan keadaan terkini dari DOM.\n>\n> Jika kita menyimpan referensi ke `elem.childNodes`, dan menambahkan/menghapus simpul pada DOM, maka simpul-simpul tersebut akan muncul secara otomatis dalam koleksi tersebut.\n\n> **PERINGATAN: Jangan menggunakan `for..in` untuk mengulangi koleksi.**\n>\n> Koleksi dapat diulangi menggunakan `for..of`. Terkadang orang lain mencoba menggunakan `for..in` untuk itu.\n>\n> Tolong, jangan melakukannya. Perulangan `for..in` mengulangi seluruh properti yang dapat dihitung (enumerable). Dan koleksi memiliki beberapa properti \"tambahan\" yang jarang digunakan yang biasanya tidak ingin kita dapatkan:\n>\n> ```html run\n> <body>\n>   <script>\n>     // menampilkan 0, 1, panjang (length), item, nilai (values), dan lain-lain.\n>     for (let prop in document.body.childNodes) alert(prop);\n>   </script>\n> </body>\n> ```\n\n## Saudara kandung (siblings) dan orang tua (parent)\n\n_Saudara kandung_ adalah simpul-simpul yang merupakan anak-anak dari orang tua yang sama.\n\nMisalnya, di sini `<head>` dan `<body>` adalah saudara kandung:\n\n```html\n<html>\n  <head>\n    ...\n  </head>\n  <body>\n    ...\n  </body>\n</html>\n```\n\n- `<body>` disebut sebagai saudara \"berikutnya\" atau \"kanan\" dari `<head>`,\n- `<head>` disebut sebagai saudara \"sebelumnya\" atau \"kiri\" dari `<body>`.\n\nSaudara berikutnya dapat diakses melalui properti `nextSibling`, dan saudara sebelumnya melalui properti `previousSibling`.\n\nOrang tua dari suatu simpul dapat diakses menggunakan properti `parentNode`.\n\nContohnya:\n\n```js run\n// Orang tua dari <body> adalah <html>\nalert(document.body.parentNode === document.documentElement); // true (benar)\n\n// Setelah <head> ada <body>\nalert(document.head.nextSibling); // HTMLBodyElement\n\n// Sebelum <body> ada <head>\nalert(document.body.previousSibling); // HTMLHeadElement\n```\n\n## Navigasi Hanya untuk Elemen\n\nProperti-navigasi yang telah disebutkan sebelumnya merujuk pada semua simpul. Misalnya, dalam `childNodes` kita bisa melihat baik simpul teks, simpul elemen, dan bahkan simpul komentar jika ada.\n\nNamun, untuk banyak tugas, kita tidak tertarik pada simpul teks atau komentar. Kita ingin memanipulasi simpul elemen yang mewakili tag dan membentuk struktur halaman.\n\nOleh karena itu, mari kita lihat lebih banyak tautan navigasi yang hanya memperhatikan _simpul elemen_:\n\n![](dom-links-elements.svg)\n\nTautan-tautan tersebut mirip dengan yang telah disebutkan sebelumnya, hanya dengan tambahan kata `Element`:\n\n- `children` -- hanya mencakup anak-anak yang merupakan simpul elemen.\n- `firstElementChild`, `lastElementChild` -- anak-anak elemen pertama dan terakhir.\n- `previousElementSibling`, `nextElementSibling` -- elemen tetangga.\n- `parentElement` -- elemen orang tua.\n\n> **Mengapa `parentElement`? Apakah orang tua bisa _tidak_ berupa elemen?**\n>\n> Properti `parentElement` mengembalikan \"elemen\" orang tua, sedangkan `parentNode` mengembalikan \"semua simpul\" orang tua. Biasanya, kedua properti tersebut akan mengambil orang tua yang sama.\n>\n> Dengan satu pengecualian yaitu `document.documentElement`:\n>\n> ```js run\n> alert(document.documentElement.parentNode); // document\n> alert(document.documentElement.parentElement); // null\n> ```\n>\n> Alasannya adalah bahwa simpul akar (root) `document.documentElement` (`<html>`) memiliki `document` sebagai orang tua. Namun, `document` bukanlah sebuah simpul elemen, sehingga `parentNode` mengembalikan `document`, sementara `parentElement` tidak.\n>\n> Detail ini bisa berguna ketika kita ingin bergerak dari suatu elemen sembarang `elem` ke `<html>`, tapi tidak ingin sampai pada `document`:\n>\n> ```js\n> while ((elem = elem.parentElement)) {\n>   // bergerak naik hingga mencapai <html>\n>   alert(elem);\n> }\n> ```\n\nMari kita modifikasi salah satu contoh di atas: gantikan `childNodes` dengan `children`. Sekarang hanya akan menampilkan elemen-elemen:\n\n```html run\n<html>\n  <body>\n    <div>Mulai</div>\n\n    <ul>\n      <li>Informasi</li>\n    </ul>\n\n    <div>Akhir</div>\n\n    <script>\n      *!*\n          for (let elem of document.body.children) {\n            alert(elem); // DIV, UL, DIV, SCRIPT\n          }\n      */!*\n    </script>\n    ...\n  </body>\n</html>\n```\n\n## Tautan-tautan Tambahan: Tabel [#dom-navigation-tables]\n\nHingga saat ini, kita telah menjelaskan properti-properti navigasi dasar.\n\nTerdapat beberapa jenis elemen DOM yang mungkin menyediakan properti tambahan, khusus untuk jenisnya, untuk kemudahan.\n\nTabel adalah contoh yang bagus untuk hal ini, dan merupakan kasus yang cukup penting:\n\n**Elemen `<table>`** mendukung (selain dari yang dijelaskan di atas) properti-properti berikut:\n\n- `table.rows` -- koleksi elemen-elemen `<tr>` dari tabel.\n- `table.caption/tHead/tFoot` -- referensi ke elemen `<caption>`, `<thead>`, `<tfoot>`.\n- `table.tBodies` -- koleksi elemen-elemen `<tbody>` (bisa banyak sesuai standar, tetapi minimal akan ada satu -- bahkan jika tidak ada dalam sumber HTML, peramban akan menambahkannya dalam DOM).\n\n**Elemen `<thead>`, `<tfoot>`, `<tbody>`** menyediakan properti `rows`:\n\n- `tbody.rows` -- koleksi elemen-elemen `<tr>` di dalamnya.\n\n**Elemen `<tr>`:**\n\n- `tr.cells` -- koleksi sel-sel dari `<td>` dan `<th>` di dalam `<tr>` yang diberikan.\n- `tr.sectionRowIndex` -- posisi (indexs) dari elemen `<tr>` tertentu di dalam elemen `<thead>/<tbody>/<tfoot>` yang melingkupinya.\n- `tr.rowIndex` -- nomor dari elemen `<tr>` di dalam tabel secara keseluruhan (termasuk semua baris tabel).\n\n**Elemen `<td>` dan `<th>`:**\n\n- `td.cellIndex` -- nomor sel di dalam elemen `<tr>`.\n\nContoh penggunaannya:\n\n```html run height=100\n<table id=\"table\">\n  <tr>\n    <td>one</td>\n    <td>two</td>\n  </tr>\n  <tr>\n    <td>three</td>\n    <td>four</td>\n  </tr>\n</table>\n\n<script>\n  // mendapatkan td dengan \"dua\" (baris pertama, kolom kedua)\n  let td = table.*!*rows[0].cells[1]*/!*;\n  td.style.backgroundColor = \"red\"; // sorotlah itu\n</script>\n```\n\nSpesifikasi: [Data Tabel](https://html.spec.whatwg.org/multipage/tables.html).\n\nTerdapat juga properti-properti navigasi tambahan untuk formulir HTML. Kita akan melihatnya nanti ketika kita mulai bekerja dengan formulir.\n\n## Ringkasan\n\nDiberikan sebuah simpul DOM, kita dapat menuju tetangganya yang langsung menggunakan properti-properti navigasi.\n\nAda dua set utama properti-properti tersebut:\n\n- Untuk semua simpul: `parentNode`, `childNodes`, `firstChild`, `lastChild`, `previousSibling`, `nextSibling`.\n- Hanya untuk elemen: `parentElement`, `children`, `firstElementChild`, `lastElementChild`, `previousElementSibling`, `nextElementSibling`.\n\nBeberapa jenis elemen DOM, misalnya tabel, menyediakan properti dan koleksi tambahan untuk mengakses kontennya.\n"
  },
  {
    "path": "2-ui/1-document/03-dom-navigation/head.html",
    "content": "<style>\n#travel-dom-comment {\n  font-style: italic;\n}\n#travel-dom-control ul {\n  margin: 6px 0;\n}\n</style>"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/solution.md",
    "content": "Ada banyak cara untuk melakukannya.\n\nIni adalah salah satu caranya:\n\n```js\n// 1. tabel dengan `id=\"age-table\"`.\nlet table = document.getElementById('age-table')\n\n// 2. Semua elemen label di dalam tabel.\ntable.getElementsByTagName('label')\n// or \ndocument.querySelectorAll('#age-table label')\n\n// 3. elemen td pertama pada tabel (dengan kata \"Age\").\ntable.rows[0].cells[0]\n// atau\ntable.getElementsByTagName('td')[0]\n// atau\ntable.querySelector('td')\n\n// 4. Formulir dengan nama \"search\".\n// mengasumsikan hanya ada satu elemen dengan nama=\"search\" di dalam dokumen.\nlet form = document.getElementsByName('search')[0]\n// atau, khususnya formulir.\ndocument.querySelector('form[name=\"search\"]')\n\n// 5. Elemen input pertama pada formulir.\nform.getElementsByTagName('input')[0]\n// atau\nform.querySelector('input')\n\n// 6. Elemen input terakhir pada formulir.\nlet inputs = form.querySelectorAll('input') // mencari semua elemen input.\ninputs[inputs.length-1] // mengambil elemen input terakhir.\n```\n"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/table.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <form name=\"search\">\n    <label>Search the site:\n      <input type=\"text\" name=\"search\">\n    </label>\n    <input type=\"submit\" value=\"Search!\">\n  </form>\n\n  <hr>\n\n  <form name=\"search-person\">\n    Search the visitors:\n    <table id=\"age-table\">\n      <tr>\n        <td>Age:</td>\n        <td id=\"age-list\">\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"young\">less than 18</label>\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"mature\">18-50</label>\n          <label>\n            <input type=\"radio\" name=\"age\" value=\"senior\">more than 50</label>\n        </td>\n      </tr>\n\n      <tr>\n        <td>Additionally:</td>\n        <td>\n          <input type=\"text\" name=\"info[0]\">\n          <input type=\"text\" name=\"info[1]\">\n          <input type=\"text\" name=\"info[2]\">\n        </td>\n      </tr>\n\n    </table>\n\n    <input type=\"submit\" value=\"Search!\">\n  </form>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/1-find-elements/task.md",
    "content": "importance: 4\n\n---\n\n# Pencarian untuk elemen\n\nIni adalah dokumen dengan tabel dan formulir.\n\nBagaimana cara mencarinya?...\n\n1. Tabel dengan `id=\"age-table\"`.\n2. Semua elemen `label` di dalam tabel (seharusnya ada 3).\n3. `td` pertama di dalam tabel (dengan kata \"Age\").\n4. `form` dengan `name=\"search\"`.\n5.  `input` pertama dalam formulir.\n6.  `input` terakhir di dalam formulir.\n\nBukalah halaman [table.html](table.html) di dalam jendela yang berbeda dan gunakan alat browser untuk itu.\n"
  },
  {
    "path": "2-ui/1-document/04-searching-elements-dom/article.md",
    "content": "# Pencarian: getElement*, querySelector*\n\nProperti navigasi DOM akan bekerja saat elemennya dekat dengan satu sama lain. Bagaimana jika tidak? Bagaimana cara mendapatkan elemen sembarang pada halaman?\n\nTerdapat metode pencarian tamabahan untuk hal ini.\n\n## document.getElementById atau hanya id\n\nJika elemen memiliki attribut `id`, kita bisa mendapatkan elemen menggunakan metode `document.getElementById(id)`, dimanapun itu berada.\n\nContoh:\n\n```html run\n<div id=\"elem\">\n  <div id=\"elem-content\">Element</div>\n</div>\n\n<script>\n  // get the element (mendapatkan elemen)\n*!*\n  let elem = document.getElementById('elem');\n*/!*\n\n  // make its background red (membuat background berwarna merah)\n  elem.style.background = 'red';\n</script>\n```\n\nDan juga, terdapat variabel global yang dinamakan `id` untuk mereferensikan elemennya:\n\n```html run\n<div id=\"*!*elem*/!*\">\n  <div id=\"*!*elem-content*/!*\">Element</div>\n</div>\n\n<script>\n  // elem is a reference to DOM-element with id=\"elem\" (elem adalah referensi dari elemen-DOM dengan id=\"elem\")\n  elem.style.background = 'red';\n\n  // id=\"elem-content\" has a hyphen inside, so it can't be a variable name (id=\"elem-content\" memiliki hypen didalam, jadi bukan nama variabel)\n  // ...but we can access it using square brackets: window['elem-content'] (...tetapi kita bisa mengakses menggunakan kurung kotak: window ['elem-content'])\n</script>\n```\n\n...Kecuali jika kita mendeklarasikan variabel Javascript dengan nama yang sama, lalu itu yang diutamakan:\n\n```html run untrusted height=0\n<div id=\"elem\"></div>\n\n<script>\n  let elem = 5; // now elem is 5, not a reference to <div id=\"elem\"> (sekarang nilai elem adalah 5, bukan referensi dari <div id=\"elem\">)\n\n  alert(elem); // 5\n</script>\n```\n\n```warn header= \"Tolong jangan gunakan variabel global dengan nama id untuk mengakses elemen\"\n\nPerilaku ini dideskripsikan pada [di spesifikasi](http://www.whatwg.org/specs/web-apps/current-work/#dom-window-nameditem), Jadi ini sudah standar. Tetapi ini didukung terutama untuk kompabilitas.\n\nBrowser berusaha membantu kita dengan mencampur namespace dari JS dan DOM. Hal ini baik baik saja untuk kode skrip sederhana, sebaris ke dalam HTML, tetapi umumnya ini tidak bagus. Bisa saja terdapat konflik penamaan. Dan juga, jika seseorang membaca kode JS tanpa melihat tampilan HTML, maka tidak akan kelihatan asal dari variabel tersebut.\n\nPada tutorial ini kita menggunakan `id` untuk mengarahkan langsung elemen supaya singkat, ketika sudah jelas darimana elemen berasal.\n\nDi dunia nyata, metode yang paling disukai adalah `document.getElementById`.\n```\n\n```smart header=\" `id` harus unik\" \n`id` harus unik. Hanya boleh ada satu elemen pada dokumen yang diberikan `id`.\n\nJika ada banyak elemen dengan `id` yang sama, maka perilaku dari metode yang digunakan tidak akan terduga, contoh `document.getElementById` akan mengembalikan elemen secara acak. Jadi tetap lakukan sesuai aturan dan buatlah `id` unik.\n```\n\n```warn header=\"Hanya `document.getElementById`, bukan `anyElem.getElementById`\"\nMetode `getElementById` yang hanya bisa di panggil pada objek `document`. ini mencari `id` yang diberikan di seluruh dokumen.\n```\n\n## querySelectorAll [#querySelectorAll]\n\nSejauh ini, metode yang paling serba guna, `elem.querySelectorAll(css)` mengembalikan semua elemen di dalam `elem` yang sama dengan *selector* CSS.\n\nDisini kita melihat semua elemen `<li>` pada anak terakhir:\n\n```html run\n<ul>\n  <li>The</li>\n  <li>test</li>\n</ul>\n<ul>\n  <li>has</li>\n  <li>passed</li>\n</ul>\n<script>\n*!*\n  let elements = document.querySelectorAll('ul > li:last-child');\n*/!*\n\n  for (let elem of elements) {\n    alert(elem.innerHTML); // \"test\", \"passed\"\n  }\n</script>\n```\n\nMetode ini memang kuat, karena bisa menggunakan *selector* CSS.\n\n```smart header=\"Bisa menggunakan kelas-pseudo\"\nJuga mendukung kelas-pseudo pada *selector* CSS seperti `:hover` dan `:active`. Contoh, `document.querySelectorAll(':hover)` akan mengembalikan koleksi dari elemen yang penunjuknya aktif sekarang(pada urutan bersarang: dari luar `<html>` hingga yang bersarang).\n```\n\n## querySelector [#querySelector]\n\nPemanggilan `elem.querySelector(css)` mengembalikan elemen pertama yang diberikan *selector* CSS.\n\nDengan kata lain, hasilnya sama dengan `elem.querySelectorAll(css)[0]`, tetapi cara ini mencari semua elemen dan memilih satu, sedangkan `elem.querySelector` hanya mencari satu. Jadi cara ini lebih cepat dan juga singkat untuk ditulis.\n\n## Persamaan\nMetode sebelumnya digunakan untuk mencari DOM.\n\nmetode [elem.matches(css)](http://dom.spec.whatwg.org/#dom-element-matches) tidak mencari apapun, metode ini hanya memeriksa apakan `elem` sama dengan *selector* CSS yang diberikan. Metode ini mengembalikan `true` atau `false`. \n\nMetode ini akan berguna saat kita mengulang elemen yang banyak (seperti *array* atau yang lain) dan mencoba untuk menyaring apa yang kita inginkan.\n\nContoh:\n\n```html run\n<a href=\"http://example.com/file.zip\">...</a>\n<a href=\"http://ya.ru\">...</a>\n\n<script>\n  // can be any collection instead of document.body.children (bisa berisi koleksi apa saja daripada document.body.children)\n  for (let elem of document.body.children) {\n*!*\n    if (elem.matches('a[href$=\"zip\"]')) {\n*/!*\n      alert(\"The archive reference: \" + elem.href );\n    }\n  }\n</script>\n```\n\n## closest\n\n*Ancestors* dari elemen adalah: *parent*, *parent* dari *parent*, *parent* dan lainnya. Ancestor yang bersama membentuk rantai *parent* dari elemen dari atas.\n\nMetode `elem.closest(css)` mencari *ancestor* terdekat yang cocok dengan *selector* CSS. `elem` sendiri juga dimasukkan pada pencarian.\n\nDengan kata lain, metode `closest` naik dari elemen dan memeriksa setiap *parent*. Jika cocok dengan *selector*, maka pencariaan akan berhenti, dan mengembalikan pada ancestor.\n\nContoh:\n\n```html run\n<h1>Contents</h1>\n\n<div class=\"contents\">\n  <ul class=\"book\">\n    <li class=\"chapter\">Chapter 1</li>\n    <li class=\"chapter\">Chapter 1</li>\n  </ul>\n</div>\n\n<script>\n  let chapter = document.querySelector('.chapter'); // LI\n\n  alert(chapter.closest('.book')); // UL\n  alert(chapter.closest('.contents')); // DIV\n\n  alert(chapter.closest('h1')); // null (because h1 is not an ancestor (karena h1 bukan ancestor)) \n</script>\n```\n\n## getElementsBy*\n\nTerdapat juga metode lainnya untuk mencari *node* dengan *tag*, kelas, dan lainnya.\n\nHari ini, kebanyakan dari metode ini menjadi sejarah, karena metode `querySelector` lebih kuat dan lebih singkat.\n\nJadi kita menjelaskannya disini untuk lebih lengkap, sementara kamu masih bisa menemukannya di kode skrip lama.\n\n- `elem.getElementsByTagName(tag)` mencari elemen dengan *tag* yang diberikan dan mengembalikan koleksi dari mereka. parameter `tag` juga bisa berupa bintang `\"*\"` untuk \"*tag* apapun\".\n- `elem.getElementsByClassName(className)` mengembalikan elemen yang diberikan kelas CSS.\n- `document.getElementsByName(name)` mengembalikan elemen dengan attribut `name`, lebar dokumen. Sangat jarang digunakan. \n\nContoh:\n```js\n// get all divs in the document (mengambil semua div pada dokumen)\nlet divs = document.getElementsByTagName('div');\n```\n\nMari kita cari semua *tag* `input` pada tabel:\n\n```html run height=50\n<table id=\"table\">\n  <tr>\n    <td>Your age:</td>\n\n    <td>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"young\" checked> less than 18\n      </label>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"mature\"> from 18 to 50\n      </label>\n      <label>\n        <input type=\"radio\" name=\"age\" value=\"senior\"> more than 60\n      </label>\n    </td>\n  </tr>\n</table>\n\n<script>\n*!*\n  let inputs = table.getElementsByTagName('input');\n*/!*\n\n  for (let input of inputs) {\n    alert( input.value + ': ' + input.checked );\n  }\n</script>\n```\n\n```warn header=\"Jangan melupakan huruf `\\\"s\\\"`\"\nPengembang pemula terkadang melupakan huruf `\"s\"`. Itu dia, mereka mencoba memanggil `getElementByTagName` daripada <code>getElement<b>s</b>ByTagName</code>.\n\nTidak ada huruf `\"s\"` pada `getElementById`, karena ini mengembalikan satu elemen. Tetapi `getElementByTagName` mengembalikan koleksi dari elemen, jadi harus ada huruf `\"s\"` didalamnya.\n```\n\n````warn header=\"Ini mengembalikan koleksi, bukan sebuah elemen!\"\nKesalahan pemula lain yang tersebar luas adalah dengan menulis:\n```js\n// doesn't work (tidak bekerja)\ndocument.getElementsByTagName('input').value = 5;\n```\n\nItu tidak akan bekerja, karena dibutuhkan koleksi dari input dan menetapkan nilai pada koleksi daripada elemen di dalamnya.\n\nKita harus melakukan iterasi dari koleksi atau mengambil elemen dari index, dan menetapkannya, seperti ini:\n\n```js\n// should work (if there's an input) akan bekerja (jika ada input nya)\ndocument.getElementsByTagName('input')[0].value = 5;\n```\n````\n\nMencari elemen `.article`:\n\n```html run height=50\n<form name=\"my-form\">\n  <div class=\"article\">Article</div>\n  <div class=\"long article\">Long article</div>\n</form>\n\n<script>\n  // find by name attribute (mencari dari nama attribut)\n  let form = document.getElementsByName('my-form')[0];\n\n  // find by class inside the form (mencari dari kelas di dalam form)\n  let articles = form.getElementsByClassName('article');\n  alert(articles.length); // 2, found two elements with class \"article\" (2, menemukan dua elemen dengan kelas \"article\")\n</script>\n```\n\n## Koleksi *Live*\n\nSemua metode `\"getElementsBy*\"` mengembalikan koleksi *live* contoh koleksi selalu mencerminkan kondisi dokumen dan \"pembaharuan otomatis\" ketika terjadi perubahan.\n\nContoh dibawah ini, ada dua kode skrip.\n1. Kode skrip pertama membuat referensi pada koleksi dari `<div>`. Untuk sekarang, panjangnya `1`.\n2. Kode skrip kedua berjalan setelah browser bertemu dengan satu `<div>` lagi, jadi panjangnya `2`.\n\n```html run\n<div>First div</div>\n\n<script>\n  let divs = document.getElementsByTagName('div');\n  alert(divs.length); // 1\n</script>\n\n<div>Second div</div>\n\n<script>\n*!*\n  alert(divs.length); // 2\n*/!*\n</script>\n```\n\nSebaliknya, `querySelectorAll` mengembalikan koleksi statis. Ini seperti *array* dari elemen yang tetap.\n\nJika kita menggunakannya, maka keluaran kedua kode skrip adalah `1`:\n\n```html run\n<div>First div</div>\n\n<script>\n  let divs = document.querySelectorAll('div');\n  alert(divs.length); // 1\n</script>\n\n<div>Second div</div>\n\n<script>\n*!*\n  alert(divs.length); // 1\n*/!*\n</script>\n```\n\nSekarang kita bisa lebih mudah melihat perbedaanya. Koleksi statis tidak bertambah setelah muncul `div` baru di dokumen.\n\n## Ringkasan\n\nTerdapat 6 metode utama untuk mencari *node* pada DOM:\n<table>\n<thead>\n<tr>\n<td>Method</td>\n<td>Searches by...</td>\n<td>Can call on an element?</td>\n<td>Live?</td>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>querySelector</code></td>\n<td>CSS-selector</td>\n<td>✔</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>querySelectorAll</code></td>\n<td>CSS-selector</td>\n<td>✔</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>getElementById</code></td>\n<td><code>id</code></td>\n<td>-</td>\n<td>-</td>\n</tr>\n<tr>\n<td><code>getElementsByName</code></td>\n<td><code>name</code></td>\n<td>-</td>\n<td>✔</td>\n</tr>\n<tr>\n<td><code>getElementsByTagName</code></td>\n<td>tag or <code>'*'</code></td>\n<td>✔</td>\n<td>✔</td>\n</tr>\n<tr>\n<td><code>getElementsByClassName</code></td>\n<td>class</td>\n<td>✔</td>\n<td>✔</td>\n</tr>\n</tbody>\n</table>\n\nSejauh ini yang paling banyak digunakan adalah `querySelector` dan `querySelectorAll`, tetapi `getElementBy*` secara terkadang membantu atau dapat ditemukan pada kode skrip lama.\n\nSelain itu:\n\n- Terdapat `elem.mathes(css)` untuk memeriksa jika `elem` cocok dengan *selector* CSS.\n- Terdapat `elem.closest(css)` untuk mencari ancestor terdekat yang cocok dengan *selector* CSS yang diberikan. `elem` juga diperiksa.\n\nDan mari kita sebutkan satu lagi metode untuk memeriksa hubungan child-*parent*, karena terkadang berguna:\n- `elemA.contains(elemB)` akan mengembalikan true jika `elemB` di dalam `elemA` adalah keturunan `elemA`) dan ketika `elemA==elemB`."
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/solution.md",
    "content": "There's a catch here.\n\nAt the time of `<script>` execution the last DOM node is exactly `<script>`, because the browser did not process the rest of the page yet.\n\nSo the result is `1` (element node).\n\n```html run height=60\n<html>\n\n<body>\n  <script>\n    alert(document.body.lastChild.nodeType);\n  </script>\n</body>\n\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-lastchild-nodetype-inline/task.md",
    "content": "importance: 5\n\n---\n\n# What's in the nodeType?\n\nWhat does the script show?\n\n```html\n<html>\n\n<body>\n  <script>\n    alert(document.body.lastChild.nodeType);\n  </script>\n</body>\n\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.md",
    "content": "Let's make a loop over `<li>`:\n\n```js\nfor (let li of document.querySelectorAll('li')) {\n  ...\n}\n```\n\nIn the loop we need to get the text inside every `li`.\n\nWe can read the text from the first child node of `li`, that is the text node:\n\n```js\nfor (let li of document.querySelectorAll('li')) {\n  let title = li.firstChild.data;\n\n  // title is the text in <li> before any other nodes\n}\n```\n\nThen we can get the number of descendants as `li.getElementsByTagName('li').length`.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    for (let li of document.querySelectorAll('li')) {\n      // get the title from the text node\n      let title = li.firstChild.data;\n\n      title = title.trim(); // remove extra spaces from ends\n\n      // get the descendants count\n      let count = li.getElementsByTagName('li').length;\n\n      alert(title + ': ' + count);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // ... your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/2-tree-info/task.md",
    "content": "importance: 5\n\n---\n\n# Count descendants\n\nThere's a tree structured as nested `ul/li`.\n\nWrite the code that for each `<li>` shows:\n\n1. What's the text inside it (without the subtree)\n2. The number of nested `<li>` -- all descendants, including the deeply nested ones.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/solution.md",
    "content": "The answer: **`BODY`**.\n\n```html run\n<script>\n  let body = document.body;\n\n  body.innerHTML = \"<!--\" + body.tagName + \"-->\";\n\n  alert( body.firstChild.data ); // BODY\n</script>\n```\n\nWhat's going on step by step:\n\n1. The content of `<body>` is replaced with the comment. The comment is `<!--BODY-->`, because `body.tagName == \"BODY\"`. As we remember, `tagName` is always uppercase in HTML.\n2. The comment is now the only child node, so we get it in `body.firstChild`.\n3. The `data` property of the comment is its contents (inside `<!--...-->`): `\"BODY\"`.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/3-tag-in-comment/task.md",
    "content": "importance: 3\n\n---\n\n# Tag in comment\n\nWhat does this code show?\n\n```html\n<script>\n  let body = document.body;\n\n  body.innerHTML = \"<!--\" + body.tagName + \"-->\";\n\n  alert( body.firstChild.data ); // what's here?\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/solution.md",
    "content": "\nWe can see which class it belongs by outputting it, like:\n\n```js run\nalert(document); // [object HTMLDocument]\n```\n\nOr:\n\n```js run\nalert(document.constructor.name); // HTMLDocument\n```\n\nSo, `document` is an instance of `HTMLDocument` class.\n\nWhat's its place in the hierarchy?\n\nYeah, we could browse the specification, but it would be faster to figure out manually.\n\nLet's traverse the prototype chain via `__proto__`.\n\nAs we know, methods of a class are in the `prototype` of the constructor. For instance, `HTMLDocument.prototype` has methods for documents.\n\nAlso, there's a reference to the constructor function inside the `prototype`:\n\n```js run\nalert(HTMLDocument.prototype.constructor === HTMLDocument); // true\n```\n\nTo get a name of the class as a string, we can use `constructor.name`. Let's do it for the whole `document` prototype chain, till class `Node`:\n\n```js run\nalert(HTMLDocument.prototype.constructor.name); // HTMLDocument\nalert(HTMLDocument.prototype.__proto__.constructor.name); // Document\nalert(HTMLDocument.prototype.__proto__.__proto__.constructor.name); // Node\n```\n\nThat's the hierarchy.\n\nWe also could examine the object using `console.dir(document)` and see these names by opening `__proto__`. The console takes them from `constructor` internally.\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/4-where-document-in-hierarchy/task.md",
    "content": "importance: 4\n\n---\n\n# Where's the \"document\" in the hierarchy?\n\nWhich class does the `document` belong to?\n\nWhat's its place in the DOM hierarchy?\n\nDoes it inherit from `Node` or `Element`, or maybe `HTMLElement`?\n"
  },
  {
    "path": "2-ui/1-document/05-basic-dom-node-properties/article.md",
    "content": "# Node properties: type, tag and contents\n\nLet's get a more in-depth look at DOM nodes.\n\nIn this chapter we'll see more into what they are and learn their most used properties.\n\n## DOM node classes\n\nDifferent DOM nodes may have different properties. For instance, an element node corresponding to tag `<a>` has link-related properties, and the one corresponding to `<input>` has input-related properties and so on. Text nodes are not the same as element nodes. But there are also common properties and methods between all of them, because all classes of DOM nodes form a single hierarchy.\n\nEach DOM node belongs to the corresponding built-in class.\n\nThe root of the hierarchy is [EventTarget](https://dom.spec.whatwg.org/#eventtarget), that is inherited by  [Node](http://dom.spec.whatwg.org/#interface-node), and other DOM nodes inherit from it.\n\nHere's the picture, explanations to follow:\n\n![](dom-class-hierarchy.svg)\n\nThe classes are:\n\n- [EventTarget](https://dom.spec.whatwg.org/#eventtarget) -- is the root \"abstract\" class. Objects of that class are never created. It serves as a base, so that all DOM nodes support so-called \"events\", we'll study them later.\n- [Node](http://dom.spec.whatwg.org/#interface-node) -- is also an \"abstract\" class, serving as a base  for DOM nodes. It provides the core tree functionality: `parentNode`, `nextSibling`, `childNodes` and so on (they are getters). Objects of `Node` class are never created. But there are concrete node classes that inherit from it, namely: `Text` for text nodes, `Element` for element nodes and more exotic ones like `Comment` for comment nodes.\n- [Element](http://dom.spec.whatwg.org/#interface-element) -- is a base class for DOM elements. It provides element-level navigation like `nextElementSibling`, `children` and searching methods like `getElementsByTagName`, `querySelector`. A browser supports not only HTML, but also XML and SVG. The `Element` class serves as a base for more specific classes: `SVGElement`, `XMLElement` and `HTMLElement`.\n- [HTMLElement](https://html.spec.whatwg.org/multipage/dom.html#htmlelement) -- is finally the basic class for all HTML elements. It is inherited by concrete HTML elements:\n    - [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) -- the class for `<input>` elements,\n    - [HTMLBodyElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlbodyelement) -- the class for `<body>` elements,\n    - [HTMLAnchorElement](https://html.spec.whatwg.org/multipage/semantics.html#htmlanchorelement) -- the class for `<a>` elements,\n    - ...and so on, each tag has its own class that may provide specific properties and methods.\n\nSo, the full set of properties and methods of a given node comes as the result of the inheritance.\n\nFor example, let's consider the DOM object for an `<input>` element. It belongs to [HTMLInputElement](https://html.spec.whatwg.org/multipage/forms.html#htmlinputelement) class.\n\nIt gets properties and methods as a superposition of (listed in inheritance order):\n\n- `HTMLInputElement` -- this class provides input-specific properties,\n- `HTMLElement` -- it provides common HTML element methods (and getters/setters),\n- `Element` -- provides generic element methods,\n- `Node` -- provides common DOM node properties,\n- `EventTarget` -- gives the support for events (to be covered),\n- ...and finally it inherits from `Object`, so \"plain object\" methods like `hasOwnProperty` are also available.\n\nTo see the DOM node class name, we can recall that an object usually has the `constructor` property. It references the class constructor, and `constructor.name` is its name:\n\n```js run\nalert( document.body.constructor.name ); // HTMLBodyElement\n```\n\n...Or we can just `toString` it:\n\n```js run\nalert( document.body ); // [object HTMLBodyElement]\n```\n\nWe also can use `instanceof` to check the inheritance:\n\n```js run\nalert( document.body instanceof HTMLBodyElement ); // true\nalert( document.body instanceof HTMLElement ); // true\nalert( document.body instanceof Element ); // true\nalert( document.body instanceof Node ); // true\nalert( document.body instanceof EventTarget ); // true\n```\n\nAs we can see, DOM nodes are regular JavaScript objects. They use prototype-based classes for inheritance.\n\nThat's also easy to see by outputting an element with `console.dir(elem)` in a browser. There in the console you can see `HTMLElement.prototype`, `Element.prototype` and so on.\n\n```smart header=\"`console.dir(elem)` versus `console.log(elem)`\"\nMost browsers support two commands in their developer tools: `console.log` and `console.dir`. They output their arguments to the console. For JavaScript objects these commands usually do the same.\n\nBut for DOM elements they are different:\n\n- `console.log(elem)` shows the element DOM tree.\n- `console.dir(elem)` shows the element as a DOM object, good to explore its properties.\n\nTry it on `document.body`.\n```\n\n````smart header=\"IDL in the spec\"\nIn the specification, DOM classes aren't described by using JavaScript, but a special [Interface description language](https://en.wikipedia.org/wiki/Interface_description_language) (IDL), that is usually easy to understand.\n\nIn IDL all properties are prepended with their types. For instance, `DOMString`, `boolean` and so on.\n\nHere's an excerpt from it, with comments:\n\n```js\n// Define HTMLInputElement\n*!*\n// The colon \":\" means that HTMLInputElement inherits from HTMLElement\n*/!*\ninterface HTMLInputElement: HTMLElement {\n  // here go properties and methods of <input> elements\n\n*!*\n  // \"DOMString\" means that the value of a property is a string\n*/!*\n  attribute DOMString accept;\n  attribute DOMString alt;\n  attribute DOMString autocomplete;\n  attribute DOMString value;\n\n*!*\n  // boolean value property (true/false)\n  attribute boolean autofocus;\n*/!*\n  ...\n*!*\n  // now the method: \"void\" means that the method returns no value\n*/!*\n  void select();\n  ...\n}\n```\n````\n\n## The \"nodeType\" property\n\nThe `nodeType` property provides one more, \"old-fashioned\" way to get the \"type\" of a DOM node.\n\nIt has a numeric value:\n- `elem.nodeType == 1` for element nodes,\n- `elem.nodeType == 3` for text nodes,\n- `elem.nodeType == 9` for the document object,\n- there are few other values in [the specification](https://dom.spec.whatwg.org/#node).\n\nFor instance:\n\n```html run\n<body>\n  <script>  \n  let elem = document.body;\n\n  // let's examine what it is?\n  alert(elem.nodeType); // 1 => element\n\n  // and the first child is...\n  alert(elem.firstChild.nodeType); // 3 => text\n\n  // for the document object, the type is 9\n  alert( document.nodeType ); // 9\n  </script>\n</body>\n```\n\nIn modern scripts, we can use `instanceof` and other class-based tests to see the node type, but sometimes `nodeType` may be simpler. We can only read `nodeType`, not change it.\n\n## Tag: nodeName and tagName\n\nGiven a DOM node, we can read its tag name from `nodeName` or `tagName` properties:\n\nFor instance:\n\n```js run\nalert( document.body.nodeName ); // BODY\nalert( document.body.tagName ); // BODY\n```\n\nIs there any difference between `tagName` and `nodeName`?\n\nSure, the difference is reflected in their names, but is indeed a bit subtle.\n\n- The `tagName` property exists only for `Element` nodes.\n- The `nodeName` is defined for any `Node`:\n    - for elements it means the same as `tagName`.\n    - for other node types (text, comment, etc.) it has a string with the node type.\n\nIn other words, `tagName` is only supported by element nodes (as it originates from `Element` class), while `nodeName` can say something about other node types.\n\nFor instance, let's compare `tagName` and `nodeName` for the `document` and a comment node:\n\n\n```html run\n<body><!-- comment -->\n\n  <script>\n    // for comment\n    alert( document.body.firstChild.tagName ); // undefined (not an element)\n    alert( document.body.firstChild.nodeName ); // #comment\n\n    // for document\n    alert( document.tagName ); // undefined (not an element)\n    alert( document.nodeName ); // #document\n  </script>\n</body>\n```\n\nIf we only deal with elements, then we can use both `tagName` and `nodeName` - there's no difference.\n\n```smart header=\"The tag name is always uppercase except in XML mode\"\nThe browser has two modes of processing documents: HTML and XML. Usually the HTML-mode is used for webpages. XML-mode is enabled when the browser receives an XML-document with the header: `Content-Type: application/xml+xhtml`.\n\nIn HTML mode `tagName/nodeName` is always uppercased: it's `BODY` either for `<body>` or `<BoDy>`.\n\nIn XML mode the case is kept \"as is\". Nowadays XML mode is rarely used.\n```\n\n\n## innerHTML: the contents\n\nThe [innerHTML](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) property allows to get the HTML inside the element as a string.\n\nWe can also modify it. So it's one of the most powerful ways to change the page.\n\nThe example shows the contents of `document.body` and then replaces it completely:\n\n```html run\n<body>\n  <p>A paragraph</p>\n  <div>A div</div>\n\n  <script>\n    alert( document.body.innerHTML ); // read the current contents\n    document.body.innerHTML = 'The new BODY!'; // replace it\n  </script>\n\n</body>\n```\n\nWe can try to insert invalid HTML, the browser will fix our errors:\n\n```html run\n<body>\n\n  <script>\n    document.body.innerHTML = '<b>test'; // forgot to close the tag\n    alert( document.body.innerHTML ); // <b>test</b> (fixed)\n  </script>\n\n</body>\n```\n\n```smart header=\"Scripts don't execute\"\nIf `innerHTML` inserts a `<script>` tag into the document -- it becomes a part of HTML, but doesn't execute.\n```\n\n### Beware: \"innerHTML+=\" does a full overwrite\n\nWe can append HTML to an element by using `elem.innerHTML+=\"more html\"`.\n\nLike this:\n\n```js\nchatDiv.innerHTML += \"<div>Hello<img src='smile.gif'/> !</div>\";\nchatDiv.innerHTML += \"How goes?\";\n```\n\nBut we should be very careful about doing it, because what's going on is *not* an addition, but a full overwrite.\n\nTechnically, these two lines do the same:\n\n```js\nelem.innerHTML += \"...\";\n// is a shorter way to write:\n*!*\nelem.innerHTML = elem.innerHTML + \"...\"\n*/!*\n```\n\nIn other words, `innerHTML+=` does this:\n\n1. The old contents is removed.\n2. The new `innerHTML` is written instead (a concatenation of the old and the new one).\n\n**As the content is \"zeroed-out\" and rewritten from the scratch, all images and other resources will be reloaded**.\n\nIn the `chatDiv` example above the line `chatDiv.innerHTML+=\"How goes?\"` re-creates the HTML content and reloads `smile.gif` (hope it's cached). If `chatDiv` has a lot of other text and images, then the reload becomes clearly visible.\n\nThere are other side-effects as well. For instance, if the existing text was selected with the mouse, then most browsers will remove the selection upon rewriting `innerHTML`. And if there was an `<input>` with a text entered by the visitor, then the text will be removed. And so on.\n\nLuckily, there are other ways to add HTML besides `innerHTML`, and we'll study them soon.\n\n## outerHTML: full HTML of the element\n\nThe `outerHTML` property contains the full HTML of the element. That's like `innerHTML` plus the element itself.\n\nHere's an example:\n\n```html run\n<div id=\"elem\">Hello <b>World</b></div>\n\n<script>\n  alert(elem.outerHTML); // <div id=\"elem\">Hello <b>World</b></div>\n</script>\n```\n\n**Beware: unlike `innerHTML`, writing to `outerHTML` does not change the element. Instead, it replaces it in the DOM.**\n\nYeah, sounds strange, and strange it is, that's why we make a separate note about it here. Take a look.\n\nConsider the example:\n\n```html run\n<div>Hello, world!</div>\n\n<script>\n  let div = document.querySelector('div');\n\n*!*\n  // replace div.outerHTML with <p>...</p>\n*/!*\n  div.outerHTML = '<p>A new element</p>'; // (*)\n\n*!*\n  // Wow! 'div' is still the same!\n*/!*\n  alert(div.outerHTML); // <div>Hello, world!</div> (**)\n</script>\n```\n\nLooks really odd, right?\n\nIn the line `(*)` we replaced `div` with `<p>A new element</p>`. In the outer document (the DOM) we can see the new content instead of the `<div>`. But, as we can see in line `(**)`, the value of the old `div` variable hasn't changed!\n\nThe `outerHTML` assignment does not modify the DOM element (the object referenced by, in this case, the variable 'div'), but removes it from the DOM and inserts the new HTML in its place.\n\nSo what happened in `div.outerHTML=...` is:\n- `div` was removed from the document.\n- Another piece of HTML `<p>A new element</p>` was inserted in its place.\n- `div` still has its old value. The new HTML wasn't saved to any variable.\n\nIt's so easy to make an error here: modify `div.outerHTML` and then continue to work with `div` as if it had the new content in it. But it doesn't. Such thing is correct for `innerHTML`, but not for `outerHTML`.\n\nWe can write to `elem.outerHTML`, but should keep in mind that it doesn't change the element we're writing to ('elem'). It puts the new HTML in its place instead. We can get references to the new elements by querying the DOM.\n\n## nodeValue/data: text node content\n\nThe `innerHTML` property is only valid for element nodes.\n\nOther node types, such as text nodes, have their counterpart: `nodeValue` and `data` properties. These two are almost the same for practical use, there are only minor specification differences. So we'll use `data`, because it's shorter.\n\nAn example of reading the content of a text node and a comment:\n\n```html run height=\"50\"\n<body>\n  Hello\n  <!-- Comment -->\n  <script>\n    let text = document.body.firstChild;\n*!*\n    alert(text.data); // Hello\n*/!*\n\n    let comment = text.nextSibling;\n*!*\n    alert(comment.data); // Comment\n*/!*\n  </script>\n</body>\n```\n\nFor text nodes we can imagine a reason to read or modify them, but why comments?\n\nSometimes developers embed information or template instructions into HTML in them, like this:\n\n```html\n<!-- if isAdmin -->\n  <div>Welcome, Admin!</div>\n<!-- /if -->\n```\n\n...Then JavaScript can read it from `data` property and process embedded instructions.\n\n## textContent: pure text\n\nThe `textContent` provides access to the *text* inside the element: only text, minus all `<tags>`.\n\nFor instance:\n\n```html run\n<div id=\"news\">\n  <h1>Headline!</h1>\n  <p>Martians attack people!</p>\n</div>\n\n<script>\n  // Headline! Martians attack people!\n  alert(news.textContent);\n</script>\n```\n\nAs we can see, only text is returned, as if all `<tags>` were cut out, but the text in them remained.\n\nIn practice, reading such text is rarely needed.\n\n**Writing to `textContent` is much more useful, because it allows to write text the \"safe way\".**\n\nLet's say we have an arbitrary string, for instance entered by a user, and want to show it.\n\n- With `innerHTML` we'll have it inserted \"as HTML\", with all HTML tags.\n- With `textContent` we'll have it inserted \"as text\", all symbols are treated literally.\n\nCompare the two:\n\n```html run\n<div id=\"elem1\"></div>\n<div id=\"elem2\"></div>\n\n<script>\n  let name = prompt(\"What's your name?\", \"<b>Winnie-the-Pooh!</b>\");\n\n  elem1.innerHTML = name;\n  elem2.textContent = name;\n</script>\n```\n\n1. The first `<div>` gets the name \"as HTML\": all tags become tags, so we see the bold name.\n2. The second `<div>` gets the name \"as text\", so we literally see `<b>Winnie-the-Pooh!</b>`.\n\nIn most cases, we expect the text from a user, and want to treat it as text. We don't want unexpected HTML in our site. An assignment to `textContent` does exactly that.\n\n## The \"hidden\" property\n\nThe \"hidden\" attribute and the DOM property specifies whether the element is visible or not.\n\nWe can use it in HTML or assign it using JavaScript, like this:\n\n```html run height=\"80\"\n<div>Both divs below are hidden</div>\n\n<div hidden>With the attribute \"hidden\"</div>\n\n<div id=\"elem\">JavaScript assigned the property \"hidden\"</div>\n\n<script>\n  elem.hidden = true;\n</script>\n```\n\nTechnically, `hidden` works the same as `style=\"display:none\"`. But it's shorter to write.\n\nHere's a blinking element:\n\n\n```html run height=50\n<div id=\"elem\">A blinking element</div>\n\n<script>\n  setInterval(() => elem.hidden = !elem.hidden, 1000);\n</script>\n```\n\n## More properties\n\nDOM elements also have additional properties, in particular those that depend on the class:\n\n- `value` -- the value for `<input>`, `<select>` and `<textarea>` (`HTMLInputElement`, `HTMLSelectElement`...).\n- `href` -- the \"href\" for `<a href=\"...\">` (`HTMLAnchorElement`).\n- `id` -- the value of \"id\" attribute, for all elements (`HTMLElement`).\n- ...and much more...\n\nFor instance:\n\n```html run height=\"80\"\n<input type=\"text\" id=\"elem\" value=\"value\">\n\n<script>\n  alert(elem.type); // \"text\"\n  alert(elem.id); // \"elem\"\n  alert(elem.value); // value\n</script>\n```\n\nMost standard HTML attributes have the corresponding DOM property, and we can access it like that.\n\nIf we want to know the full list of supported properties for a given class, we can find them in the specification. For instance, `HTMLInputElement` is documented at <https://html.spec.whatwg.org/#htmlinputelement>.\n\nOr if we'd like to get them fast or are interested in a concrete browser specification -- we can always output the element using `console.dir(elem)` and read the properties. Or explore \"DOM properties\" in the Elements tab of the browser developer tools.\n\n## Summary\n\nEach DOM node belongs to a certain class. The classes form a hierarchy. The full set of properties and methods come as the result of inheritance.\n\nMain DOM node properties are:\n\n`nodeType`\n: We can use it to see if a node is a text or an element node. It has a numeric value: `1` for elements,`3` for text nodes, and a few others for other node types. Read-only.\n\n`nodeName/tagName`\n: For elements, tag name (uppercased unless XML-mode). For non-element nodes `nodeName` describes what it is. Read-only.\n\n`innerHTML`\n: The HTML content of the element. Can be modified.\n\n`outerHTML`\n: The full HTML of the element. A write operation into `elem.outerHTML` does not touch `elem` itself. Instead it gets replaced with the new HTML in the outer context.\n\n`nodeValue/data`\n: The content of a non-element node (text, comment). These two are almost the same, usually we use `data`. Can be modified.\n\n`textContent`\n: The text inside the element: HTML minus all `<tags>`. Writing into it puts the text inside the element, with all special characters and tags treated exactly as text. Can safely insert user-generated text and protect from unwanted HTML insertions.\n\n`hidden`\n: When set to `true`, does the same as CSS `display:none`.\n\nDOM nodes also have other properties depending on their class. For instance, `<input>` elements (`HTMLInputElement`) support `value`, `type`, while `<a>` elements (`HTMLAnchorElement`) support `href` etc. Most standard HTML attributes have a corresponding DOM property.\n\nHowever, HTML attributes and DOM properties are not always the same, as we'll see in the next chapter.\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/solution.md",
    "content": "\n```html run height=100\n<!DOCTYPE html>\n<html>\n<body>\n\n  <div data-widget-name=\"menu\">Choose the genre</div>\n\n  <script>\n    // getting it\n    let elem = document.querySelector('[data-widget-name]');\n\n    // reading the value\n    alert(elem.dataset.widgetName);\n    // or\n    alert(elem.getAttribute('data-widget-name'));\n  </script>\n</body>\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/1-get-user-attribute/task.md",
    "content": "importance: 5\n\n---\n\n# Get the attribute\n\nWrite the code to select the element with `data-widget-name` attribute from the document and to read its value.\n\n```html run\n<!DOCTYPE html>\n<html>\n<body>\n\n  <div data-widget-name=\"menu\">Choose the genre</div>\n\n  <script>\n    /* your code */\n  </script>\n</body>\n</html>\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.md",
    "content": "\nFirst, we need to find all external references.\n\nThere are two ways.\n\nThe first is to find all links using `document.querySelectorAll('a')` and then filter out what we need:\n\n```js\nlet links = document.querySelectorAll('a');\n\nfor (let link of links) {\n*!*\n  let href = link.getAttribute('href');\n*/!*\n  if (!href) continue; // no attribute\n\n  if (!href.includes('://')) continue; // no protocol\n\n  if (href.startsWith('http://internal.com')) continue; // internal\n\n  link.style.color = 'orange';\n}\n```\n\nPlease note: we use `link.getAttribute('href')`. Not `link.href`, because we need the value from HTML.\n\n...Another, simpler way would be to add the checks to CSS selector:\n\n```js\n// look for all links that have :// in href\n// but href doesn't start with http://internal.com\nlet selector = 'a[href*=\"://\"]:not([href^=\"http://internal.com\"])';\nlet links = document.querySelectorAll(selector);\n\nlinks.forEach(link => link.style.color = 'orange');\n```\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <a name=\"list\">The list:</a>\n  <ul>\n    <li><a href=\"http://google.com\">http://google.com</a></li>\n    <li><a href=\"/tutorial\">/tutorial.html</a></li>\n    <li><a href=\"local/path\">local/path</a></li>\n    <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n    <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n    <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n  </ul>\n\n  <script>\n    let selector = 'a[href*=\"://\"]:not([href^=\"http://internal.com\"])';\n    let links = document.querySelectorAll(selector);\n\n    links.forEach(link => link.style.color = 'orange');\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <a name=\"list\">The list:</a>\n  <ul>\n    <li><a href=\"http://google.com\">http://google.com</a></li>\n    <li><a href=\"/tutorial\">/tutorial.html</a></li>\n    <li><a href=\"local/path\">local/path</a></li>\n    <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n    <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n    <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n  </ul>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/2-yellow-links/task.md",
    "content": "importance: 3\n\n---\n\n# Make external links orange\n\nMake all external links orange by altering their `style` property.\n\nA link is external if:\n- Its `href` has `://` in it\n- But doesn't start with `http://internal.com`.\n\nExample:\n\n```html run\n<a name=\"list\">the list</a>\n<ul>\n  <li><a href=\"http://google.com\">http://google.com</a></li>\n  <li><a href=\"/tutorial\">/tutorial.html</a></li>\n  <li><a href=\"local/path\">local/path</a></li>\n  <li><a href=\"ftp://ftp.com/my.zip\">ftp://ftp.com/my.zip</a></li>\n  <li><a href=\"http://nodejs.org\">http://nodejs.org</a></li>\n  <li><a href=\"http://internal.com/test\">http://internal.com/test</a></li>\n</ul>\n\n<script>\n  // setting style for a single link\n  let link = document.querySelector('a');\n  link.style.color = 'orange';\n</script>\n```\n\nThe result should be:\n\n[iframe border=1 height=180 src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/06-dom-attributes-and-properties/article.md",
    "content": "# Atribut dan Properti\n\nKetika peramban (browser) memuat halaman, peramban \"membaca\" (atau istilah lainnya: \"menganalisis\") HTML dan menghasilkan objek-objek DOM dari HTML tersebut. Untuk elemen-elemen, sebagian besar atribut HTML standar secara otomatis menjadi properti dari objek-objek DOM.\n\nMisalnya, jika tag adalah `<body id=\"page\">`, maka objek DOM akan memiliki `body.id=\"page\"`.\n\nNamun, pemetaan atribut-atribut ini tidaklah satu-satu! Di bab ini, kita akan memahami perbedaan antara kedua hal tersebut, untuk melihat bagaimana cara bekerja dengan mereka, ketika mereka sama, dan ketika mereka berbeda.\n\n## Properti DOM\n\nKita telah melihat beberapa properti DOM bawaan sebelumnya. Ada banyak properti lainnya. Tetapi secara teknis, tidak ada batasan, dan jika tidak cukup, kita dapat menambahkan properti sendiri.\n\nSimpul DOM adalah objek JavaScript biasa. Kita dapat memodifikasinya.\n\nMisalnya, mari bat sebuah properti baru pada `document.body`:\n\n```js run\ndocument.body.myData = {\n  name: \"Caesar\",\n  title: \"Imperator\",\n};\n\nalert(document.body.myData.title); // Imperator\n```\n\nKita juga dapat menambahkan sebuah method:\n\n```js run\ndocument.body.sayTagName = function () {\n  alert(this.tagName);\n};\n\ndocument.body.sayTagName(); // BODY (nilai dari \"this\" dalam method adalah document.body)\n```\n\nKita juga dapat memodifikasi prototipe bawaan seperti `Element.prototype` dan menambahkan method baru ke semua elemen:\n\n```js run\nElement.prototype.sayHi = function () {\n  alert(`Hello, I'm ${this.tagName}`);\n};\n\ndocument.documentElement.sayHi(); // Hello, I'm HTML\ndocument.body.sayHi(); // Hello, I'm BODY\n```\n\nJadi, properti dan method DOM berperilaku sama seperti objek JavaScript biasa:\n\n- Mereka dapat memiliki nilai apa pun.\n- Mereka bersifat case-sensitive (tulis `elem.nodeType`, bukan `elem.NoDeTyPe`).\n\n## Atribut HTML\n\nDalam HTML, tag-tag dapat memiliki atribut. Ketika peramban mengurai HTML untuk membuat objek-objek DOM untuk tag-tag tersebut, peramban mengenali atribut-atribut _standar_ dan membuat properti DOM dari atribut-atribut tersebut.\n\nJadi, ketika sebuah elemen memiliki atribut `id` atau atribut _standar_ lainnya, properti yang sesuai akan dibuat. Namun, hal ini tidak terjadi jika atribut tersebut adalah non-standar.\n\nMisalnya:\n\n```html run\n<body id=\"test\" something=\"non-standard\">\n  <script>\n        alert(document.body.id); // test\n    *!*\n        // atribut non-standar tidak menghasilkan properti\n        alert(document.body.something); // undefined\n    */!*\n  </script>\n</body>\n```\n\nHarap dicatat bahwa atribut standar untuk satu elemen dapat tidak dikenali oleh elemen lain. Misalnya, `\"type\"` adalah atribut standar untuk `<input>` ([HTMLInputElement](https://html.spec.whatwg.org/#htmlinputelement)), tetapi bukan untuk `<body>` ([HTMLBodyElement](https://html.spec.whatwg.org/#htmlbodyelement)). Atribut-atribut standar dijelaskan dalam spesifikasi untuk kelas elemen yang sesuai.\n\nDi sini kita dapat melihatnya:\n\n```html run\n<body id=\"body\" type=\"...\">\n  <input id=\"input\" type=\"text\" />\n  <script>\n        alert(input.type); // text\n    *!*\n        alert(body.type); // undefined: properti DOM tidak dibuat, karena itu adalah non-standar\n    */!*\n  </script>\n</body>\n```\n\nJadi, jika sebuah atribut non-standar, maka tidak akan ada properti DOM untuknya. Apakah ada cara untuk mengakses atribut-atribut tersebut?\n\nTentu saja. Semua atribut dapat diakses dengan menggunakan method-method berikut:\n\n- `elem.hasAttribute(name)` -- memeriksa keberadaan atribut.\n- `elem.getAttribute(name)` -- mendapatkan nilainya.\n- `elem.setAttribute(name, value)` -- mengatur nilainya.\n- `elem.removeAttribute(name)` -- menghapus atribut.\n\nMethod-method ini beroperasi sesuai dengan apa yang tertulis dalam HTML.\n\nSelain itu, kita dapat membaca semua atribut menggunakan `elem.attributes`: sebuah koleksi objek yang termasuk ke dalam kelas bawaan [Attr](https://dom.spec.whatwg.org/#attr), dengan properti `name` dan `value`.\n\nBerikut adalah contoh membaca atribut non-standar:\n\n```html run\n<body something=\"non-standard\">\n  <script>\n    *!*\n        alert(document.body.getAttribute('something')); // non-standar\n    */!*\n  </script>\n</body>\n```\n\nAtribut HTML memiliki fitur-fitur berikut:\n\n- Nama mereka bersifat case-insensitive (`id` sama dengan `ID`).\n- Nilai-nilai mereka selalu berupa string.\n\nBerikut adalah contoh lebih lanjut tentang cara bekerja dengan atribut-atribut:\n\n```html run\n<body>\n  <div id=\"elem\" about=\"Elephant\"></div>\n\n  <script>\n    alert(elem.getAttribute(\"About\")); // (1) 'Elephant', membaca\n\n    elem.setAttribute(\"Test\", 123); // (2), menulis\n\n    alert(elem.outerHTML); // (3), lihat apakah atributnya ada di HTML (ya)\n\n    for (let attr of elem.attributes) {\n      // (4) daftar semua\n      alert(`${attr.name} = ${attr.value}`);\n    }\n  </script>\n</body>\n```\n\nHarap dicatat:\n\n1. `getAttribute('About')` -- huruf pertama di sini adalah huruf kapital, dan dalam HTML semuanya huruf kecil. Tetapi hal ini tidak masalah: nama atribut bersifat case-insensitive.\n2. Kita dapat menetapkan apa pun sebagai atribut, tetapi nilainya akan menjadi sebuah string. Jadi disini kita memiliki `\"123\"` sebagai nilai atribut.\n3. Semua atribut, termasuk yang kita set, terlihat dalam `outerHTML`.\n4. Koleksi `attributes` dapat diulangi (iterable) dan berisi semua atribut dari elemen tersebut (standar dan non-standar) sebagai objek dengan properti `name` dan `value`.\n\n## Sinkronisasi Properti-atribut\n\nKetika atribut standar berubah, properti yang sesuai akan diperbarui secara otomatis, dan (dengan beberapa pengecualian) sebaliknya.\n\nPada contoh dibawah ini, `id` diubah sebagai atribut, dan kita dapat melihat bahwa properti juga berubah. Kemudian, hal yang sama terjadi sebaliknya:\n\n```html run\n<input />\n\n<script>\n  let input = document.querySelector(\"input\");\n\n  // atribut => properti\n  input.setAttribute(\"id\", \"id\");\n  alert(input.id); // id (diperbarui)\n\n  // properti => atribut\n  input.id = \"newId\";\n  alert(input.getAttribute(\"id\")); // newId (diperbarui)\n</script>\n```\n\nTetapi ada pengecualian, misalnya `input.value` disinkronkan hanya dari atribut -> ke properti, tapi tidak sebaliknya:\n\n```html run\n<input />\n\n<script>\n    let input = document.querySelector('input');\n\n    // atribut => properti\n    input.setAttribute('value', 'text');\n    alert(input.value); // text\n\n  *!*\n    // BUKAN properti => atribut\n    input.value = 'newValue';\n    alert(input.getAttribute('value')); // teks (tidak diperbarui!)\n  */!*\n</script>\n```\n\nPada contoh di atas:\n\n- Mengubah atribut `value` akan memperbarui propertinya.\n- Tetapi perubahan properti tidak mempengarui atributnya.\n\n\"Fitur\" tersebut sebenarnya bisa sangat berguna, karena tindakan pengguna dapat menyebabkan perubahan nilai `value`, dan kemudian, jika kita ingin mengembalikan nilai \"asli\" dari HTML, nilainya terdapat dalam atribut.\n\n## Properti DOM bertipe\n\nProperti DOM tidak selalu berupa string. Misalnya, properti `input.checked` (untuk kotak centang / checkbox) adalah boolean:\n\n```html run\n<input id=\"input\" type=\"checkbox\" checked /> checkbox\n\n<script>\n  alert(input.getAttribute(\"checked\")); // nilai atributnya adalah: string kosong\n  alert(input.checked); // nilai propertinya adalah: benar (true)\n</script>\n```\n\nAda contoh lain. Atribut `style` adalah string, tetapi properti `style` adalah objek:\n\n```html run\n<div id=\"div\" style=\"color:red;font-size:120%\">Hello</div>\n\n<script>\n  // string\n  alert(div.getAttribute(\"style\")); // color:red;font-size:120%\n\n  // objek\n  alert(div.style); // [object CSSStyleDeclaration]\n  alert(div.style.color); // red\n</script>\n```\n\nSebagian besar properti adalah string.\n\nJarang sekali, meskipun tipe properti DOM adalah string, itu dapat berbeda dari atribut. Misalnya, properti DOM `href` selalu berupa URL _penuh_, meskipun atributnya berisi URL relatif atau hanya `#hash`.\n\nBerikut adalah contoh:\n\n```html height=30 run\n<a id=\"a\" href=\"#hello\">link</a>\n<script>\n  // atribut\n  alert(a.getAttribute(\"href\")); // #hello\n\n  // properti\n  alert(a.href); // URL penuh dalam formulir http://site.com/page#hello\n</script>\n```\n\nJika kita membutuhkan nilai `href` atau atribut lainnya secara tepat seperti yang tertulis dalam HTML, kita dapat menggunakan `getAttribute`.\n\n## Atribut non-standar, dataset\n\nKetika menulis HTML, kita sering menggunakan atribut-atribut standar. Tetapi bagaimana dengan atribut non-standar, khusus? Pertama, mari lihat apakah mereka bermanfaat atau tidak? Untuk apa?\n\nTerkadang atribut non-standar digunakan untuk menyampaikan data kustom dari HTML ke JavaScript, atau untuk \"menandai\" elemen-elemen HTML untuk JavaScript.\n\nContohnya seperti ini:\n\n```html run\n<!-- berikan tanda pada `div` untuk menampilkan \"name\" di sini -->\n<div *!*show-info=\"name\"*/!*></div>\n<!-- dan \"umur\" di sini -->\n<div *!*show-info=\"age\"*/!*></div>\n\n<script>\n  // kode tersebut menemukan elemen dengan tanda dan menampilkan apa yang di minta\n  let user = {\n    name: \"Pete\",\n    age: 25\n  };\n\n  for(let div of document.querySelectorAll('[show-info]')) {\n    // sisipkan info yang sesuai ke dalam elemen\n    let field = div.getAttribute('show-info');\n    div.innerHTML = user[field]; // pertama Pete ke dalam \"name\", kemudian 25 ke dalam \"age\"\n  }\n</script>\n```\n\nSelain itu, atribut non-standar juga dapat digunakan untuk memberikan gaya (style) pada elemen.\n\nMisalnya, di sini untuk keadaan pesanan (order state), digunakan atribut `order-state`:\n\n```html run\n<style>\n  /* gaya-gaya (styles) mengandalkan atribut \"order-state\" */\n  .order[order-state=\"new\"] {\n    color: green;\n  }\n\n  .order[order-state=\"pending\"] {\n    color: blue;\n  }\n\n  .order[order-state=\"canceled\"] {\n    color: red;\n  }\n</style>\n\n<div class=\"order\" order-state=\"new\">A new order.</div>\n\n<div class=\"order\" order-state=\"pending\">A pending order.</div>\n\n<div class=\"order\" order-state=\"canceled\">A canceled order.</div>\n```\n\nMengapa menggunakan atribut lebih disukai daripada menggunakan kelas seperti `.order-state-new`, `.order-state-pending`, `.order-state-canceled`?\n\nKarena atribut lebih mudah dikelola. Keadaan dapat diubah dengan mudah seperti ini:\n\n```js\n// sedikit lebih sederhana daripada menghapus kelas lama/menambahkan kelas baru\ndiv.setAttribute(\"order-state\", \"canceled\");\n```\n\nNamun, ada masalah yang mungkin muncul dengan atribut kustom. Bagaimana jika kita menggunakan atribut non-standar untuk tujuan kita dan kemudian standar memperkenalkannya dan memberikan fungsionalitas tertentu padanya? Bahasa HTML adalah dinamis, berkembang, dan atribut-atribut baru muncul untuk memenuhi kebutuhan para pengembang. Dalam kasus tersebut, dapat terjadi efek yang tidak terduga.\n\nUntuk menghindari konflik, ada [data-\\*](https://html.spec.whatwg.org/#embedding-custom-non-visible-data-with-the-data-*-attributes) atribut.\n\n**Semua atribut yang dimulai dengan \"data-\" disediakan untuk penggunaan programmer. Mereka dapat di akses melalui properti `dataset`.**\n\nMisalnya, jika sebuah `elem` memiliki atribut bernama `\"data-about\"`, maka dapat diakses menggunakan `elem.dataset.about`.\n\nContohnya seperti ini:\n\n```html run\n<body data-about=\"Elephants\">\n  <script>\n    alert(document.body.dataset.about); // Elephants\n  </script>\n</body>\n```\n\nAtribut dengan beberapa kata seperti `data-order-state` akan menjadi camel-cased: `dataset.orderState`.\n\nBerikut adalah contoh yang telah diperbaiki untuk \"order state\":\n\n```html run\n<style>\n  .order[data-order-state=\"new\"] {\n    color: green;\n  }\n\n  .order[data-order-state=\"pending\"] {\n    color: blue;\n  }\n\n  .order[data-order-state=\"canceled\"] {\n    color: red;\n  }\n</style>\n\n<div id=\"order\" class=\"order\" data-order-state=\"new\">A new order.</div>\n\n<script>\n  // membaca\n  alert(order.dataset.orderState); // baru\n\n  // memodifikasi\n  order.dataset.orderState = \"pending\"; // (*)\n</script>\n```\n\nMenggunakan atribut `data-*` adalah cara yang valid dan aman untuk menyampaikan data kustom.\n\nHarap dicatat bahwa kita tidak hanya bisa membaca, tetapi juga mengubah atribut data. Selanjutnya, CSS akan memperbarui tampilan sesuai dengan perubahan tersebut: pada contoh di atas, baris terakhir `(*)` mengubah warna menjadi biru.\n\n## Ringkasan\n\n- Atribut -- adalah apa yang tertulis dalam HTML.\n- Properti -- adalah apa yang ada dalam objek DOM.\n\nSebuah perbandingan kecil:\n\n|      | Properti                                                                                     | Atribut                            |\n| ---- | -------------------------------------------------------------------------------------------- | ---------------------------------- |\n| Tipe | Bisa memiliki nilai apapun, properti standar memiliki tipe yang dijelaskan dalam spesifikasi | Sebuah string                      |\n| Nama | Nama bersifat case-sensitive                                                                 | Nama tidak bersifat case-sensitive |\n\nMethod-method untuk bekerja dengan atribut adalah:\n\n- `elem.hasAttribute(name)` -- untuk memeriksa keberadaan atribut.\n- `elem.getAttribute(name)` -- untuk mendapatkan nilai atribut.\n- `elem.setAttribute(name, value)` -- untuk mengatur nilai atribut.\n- `elem.removeAttribute(name)` -- untuk menghapus atribut.\n- `elem.attributes` adalah koleksi dari semua atribut.\n\nUntuk sebagian besar situasi, menggunakan properti DOM lebih disukai. Kita harus merujuk pada atribut hanya ketika properti DOM tidak sesuai dengan kebutuhan kita, ketika kita memerlukan atribut secara khusus, misalnya:\n\n- Kita membutuhkan atribut non-standar. Tetapi jika atribut tersebut dimulai dengan `data-`, maka kita harus menggunakan `dataset`.\n- Kita ingin membaca nilai \"sebagaimana tertulis\" dalam HTML. Nilai properti DOM mungkin berbeda, misalnya properti `href` selalu berupa URL lengkap, dan kita mungkin ingin mendapatkan nilai \"asli\" tersebut.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/solution.md",
    "content": "Answer: **1 and 3**.\n\nBoth commands result in adding the `text` \"as text\" into the `elem`.\n\nHere's an example:\n\n```html run height=80\n<div id=\"elem1\"></div>\n<div id=\"elem2\"></div>\n<div id=\"elem3\"></div>\n<script>\n  let text = '<b>text</b>';\n\n  elem1.append(document.createTextNode(text));\n  elem2.innerHTML = text;\n  elem3.textContent = text;\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/1-createtextnode-vs-innerhtml/task.md",
    "content": "importance: 5\n\n---\n\n# createTextNode vs innerHTML vs textContent\n\nWe have an empty DOM element `elem` and a string `text`.\n\nWhich of these 3 commands will do exactly the same?\n\n1. `elem.append(document.createTextNode(text))`\n2. `elem.innerHTML = text`\n3. `elem.textContent = text`\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.md",
    "content": "First, let's make HTML/CSS.\n\nEach component of the time would look great in its own `<span>`:\n\n```html\n<div id=\"clock\">\n  <span class=\"hour\">hh</span>:<span class=\"min\">mm</span>:<span class=\"sec\">ss</span>\n</div>\n```\n\nAlso we'll need CSS to color them.\n\nThe `update` function will refresh the clock, to be called by `setInterval` every second:\n\n```js\nfunction update() {\n  let clock = document.getElementById('clock');\n*!*\n  let date = new Date(); // (*)\n*/!*\n  let hours = date.getHours();\n  if (hours < 10) hours = '0' + hours;\n  clock.children[0].innerHTML = hours;\n\n  let minutes = date.getMinutes();\n  if (minutes < 10) minutes = '0' + minutes;\n  clock.children[1].innerHTML = minutes;\n\n  let seconds = date.getSeconds();\n  if (seconds < 10) seconds = '0' + seconds;\n  clock.children[2].innerHTML = seconds;\n}\n```\n\nIn the line `(*)` we every time check the current date. The calls to `setInterval` are not reliable: they may happen with delays.\n\nThe clock-managing functions:\n\n```js\nlet timerId;\n\nfunction clockStart() { // run the clock  \n  if (!timerId) { // only set a new interval if the clock is not running\n    timerId = setInterval(update, 1000);\n  }\n  update(); // (*)\n}\n\nfunction clockStop() {\n  clearInterval(timerId);\n  timerId = null; // (**)\n}\n```\n\nPlease note that the call to `update()` is not only scheduled in `clockStart()`, but immediately run in the line `(*)`. Otherwise the visitor would have to wait till the first execution of `setInterval`. And the clock would be empty till then.\n\nAlso it is important to set a new interval in `clockStart()` only when the clock is not running. Otherways clicking the start button several times would set multiple concurrent intervals. Even worse - we would only keep the `timerID` of the last interval, losing references to all others. Then we wouldn't be able to stop the clock ever again! Note that we need to clear the `timerID` when the clock is stopped in the line `(**)`, so that it can be started again by running `clockStart()`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    .hour {\n      color: red\n    }\n\n    .min {\n      color: green\n    }\n\n    .sec {\n      color: blue\n    }\n  </style>\n</head>\n\n<body>\n\n  <div id=\"clock\">\n    <span class=\"hour\">hh</span>:<span class=\"min\">mm</span>:<span class=\"sec\">ss</span>\n  </div>\n\n  <script>\n    let timerId;\n\n    function update() {\n      let clock = document.getElementById('clock');\n      let date = new Date();\n\n      let hours = date.getHours();\n      if (hours < 10) hours = '0' + hours;\n      clock.children[0].innerHTML = hours;\n\n      let minutes = date.getMinutes();\n      if (minutes < 10) minutes = '0' + minutes;\n      clock.children[1].innerHTML = minutes;\n\n      let seconds = date.getSeconds();\n      if (seconds < 10) seconds = '0' + seconds;\n      clock.children[2].innerHTML = seconds;\n    }\n\n    function clockStart() {\n      // set a new interval only if the clock is stopped\n      // otherwise we would rewrite the timerID reference to the running interval and wouldn't be able to stop the clock ever again\n      if (!timerId) { \n        timerId = setInterval(update, 1000);\n      }\n      update(); // <--  start right now, don't wait 1 second till the first setInterval works\n    }\n\n    function clockStop() {\n      clearInterval(timerId);\n      timerId = null; // <-- clear timerID to indicate that the clock has been stopped, so that it is possible to start it again in clockStart()\n    }\n\n    clockStart();\n  </script>\n\n  <!-- click on this button calls clockStart() -->\n  <input type=\"button\" onclick=\"clockStart()\" value=\"Start\">\n\n  <!-- click on this button calls clockStop() -->\n  <input type=\"button\" onclick=\"clockStop()\" value=\"Stop\">\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <!-- click on this button calls clockStart() -->\n  <input type=\"button\" onclick=\"clockStart()\" value=\"Start\">\n\n  <!-- click on this button calls clockStop() -->\n  <input type=\"button\" onclick=\"clockStop()\" value=\"Stop\">\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/10-clock-setinterval/task.md",
    "content": "importance: 4\n\n---\n\n# Colored clock with setInterval\n\nCreate a colored clock like here:\n\n[iframe src=\"solution\" height=60]\n\nUse HTML/CSS for the styling, JavaScript only updates time in elements.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/11-append-to-list/solution.md",
    "content": "\nWhen we need to insert a piece of HTML somewhere, `insertAdjacentHTML` is the best fit.\n  \nThe solution:\n\n```js\none.insertAdjacentHTML('afterend', '<li>2</li><li>3</li>');\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/11-append-to-list/task.md",
    "content": "importance: 5\n\n---\n\n# Insert the HTML in the list\n\nWrite the code to insert `<li>2</li><li>3</li>` between two `<li>` here:\n\n```html\n<ul id=\"ul\">\n  <li id=\"one\">1</li>\n  <li id=\"two\">4</li>\n</ul>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/solution.md",
    "content": "The solution is short, yet may look a bit tricky, so here I provide it with extensive comments:\n\n```js\nlet sortedRows = Array.from(table.tBodies[0].rows) // 1\n  .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));\n\ntable.tBodies[0].append(...sortedRows); // (3)\n```\n\nThe step-by-step algorthm:\n\n1. Get all `<tr>`, from `<tbody>`.\n2. Then sort them comparing by the content of the first `<td>` (the name field).\n3. Now insert nodes in the right order by `.append(...sortedRows)`.\n\nWe don't have to remove row elements, just \"re-insert\", they leave the old place automatically.\n\nP.S. In our case, there's an explicit `<tbody>` in the table, but even if HTML table doesn't have `<tbody>`, the DOM structure always has it.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/solution.view/index.html",
    "content": "<!DOCTYPE html>\n\n<table id=\"table\">\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n\n<script>\n  let sortedRows = Array.from(table.tBodies[0].rows)\n    .sort((rowA, rowB) => rowA.cells[0].innerHTML.localeCompare(rowB.cells[0].innerHTML));\n\n  table.tBodies[0].append(...sortedRows);\n</script>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/source.view/index.html",
    "content": "<!DOCTYPE html>\n\n<table id=\"table\">\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n\n<script>\n  // ... your code ...\n</script>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/12-sort-table/task.md",
    "content": "importance: 5\n\n---\n\n# Sort the table\n\nThere's a table:\n\n```html run\n<table>\n<thead>\n  <tr>\n    <th>Name</th><th>Surname</th><th>Age</th>\n  </tr>\n</thead>\n<tbody>\n  <tr>\n    <td>John</td><td>Smith</td><td>10</td>\n  </tr>\n  <tr>\n    <td>Pete</td><td>Brown</td><td>15</td>\n  </tr>\n  <tr>\n    <td>Ann</td><td>Lee</td><td>5</td>\n  </tr>\n  <tr>\n    <td>...</td><td>...</td><td>...</td>\n  </tr>\n</tbody>\n</table>\n```\n\nThere may be more rows in it.\n\nWrite the code to sort it by the `\"name\"` column.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/4-clear-elem/solution.md",
    "content": "\nFirst, let's see how *not* to do it:\n\n```js\nfunction clear(elem) {\n  for (let i=0; i < elem.childNodes.length; i++) {\n      elem.childNodes[i].remove();\n  }\n}\n```\n\nThat won't work, because the call to `remove()` shifts the collection `elem.childNodes`, so elements start from the index `0` every time. But `i` increases, and some elements will be skipped.\n\nThe `for..of` loop also does the same.\n\nThe right variant could be:\n\n```js\nfunction clear(elem) {\n  while (elem.firstChild) {\n    elem.firstChild.remove();\n  }\n}\n```\n\nAnd also there's a simpler way to do the same:\n\n```js\nfunction clear(elem) {\n  elem.innerHTML = '';\n}\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/4-clear-elem/task.md",
    "content": "importance: 5\n\n---\n\n# Clear the element\n\nCreate a function `clear(elem)` that removes everything from the element.\n\n```html run height=60\n<ol id=\"elem\">\n  <li>Hello</li>\n  <li>World</li>\n</ol>\n\n<script>\n  function clear(elem) { /* your code */ }\n\n  clear(elem); // clears the list\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/5-why-aaa/solution.md",
    "content": "The HTML in the task is incorrect. That's the reason of the odd thing.\n\nThe browser has to fix it automatically. But there may be no text inside the `<table>`: according to the spec only table-specific tags are allowed. So the browser shows `\"aaa\"` *before* the `<table>`.\n\nNow it's obvious that when we remove the table, it remains.\n\nThe question can be easily answered by exploring the DOM using the browser tools. You'll see `\"aaa\"` before the `<table>`.\n\nThe HTML standard specifies in detail how to process bad HTML, and such behavior of the browser is correct.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/5-why-aaa/task.md",
    "content": "importance: 1\n\n---\n\n# Why does \"aaa\" remain?\n\nIn the example below, the call `table.remove()` removes the table from the document.\n\nBut if you run it, you can see that the text `\"aaa\"` is still visible.\n\nWhy does that happen?\n\n```html height=100 run\n<table id=\"table\">\n  aaa\n  <tr>\n    <td>Test</td>\n  </tr>\n</table>\n\n<script>\n  alert(table); // the table, as it should be\n\n  table.remove();\n  // why there's still aaa in the document?\n</script>\n```\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/solution.md",
    "content": "Please note the usage of `textContent` to assign the `<li>` content.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <h1>Create a list</h1>\n\n  <script>\n    let ul = document.createElement('ul');\n    document.body.append(ul);\n\n    while (true) {\n      let data = prompt(\"Enter the text for the list item\", \"\");\n\n      if (!data) {\n        break;\n      }\n\n      let li = document.createElement('li');\n      li.textContent = data;\n      ul.append(li);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/6-create-list/task.md",
    "content": "importance: 4\n\n---\n\n# Create a list\n\nWrite an interface to create a list from user input.\n\nFor every list item:\n\n1. Ask a user about its content using `prompt`.\n2. Create the `<li>` with it and add it to `<ul>`.\n3. Continue until the user cancels the input (by pressing `key:Esc` or via an empty entry).\n\nAll elements should be created dynamically.\n\nIf a user types HTML-tags, they should be treated like a text.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/build-tree-dom.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n\n  <div id=\"container\"></div>\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, obj) {\n      container.append(createTreeDom(obj));\n    }\n\n    function createTreeDom(obj) {\n      // if there's no children, then the call returns undefined\n      // and the <ul> won't be created\n      if (!Object.keys(obj).length) return;\n\n      let ul = document.createElement('ul');\n\n      for (let key in obj) {\n        let li = document.createElement('li');\n        li.innerHTML = key;\n\n        let childrenUl = createTreeDom(obj[key]);\n        if (childrenUl) {\n          li.append(childrenUl);\n        }\n\n        ul.append(li);\n      }\n\n      return ul;\n    }\n\n    let container = document.getElementById('container');\n    createTree(container, data);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/innerhtml.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<body>\n\n  <div id=\"container\"></div>\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, obj) {\n      container.innerHTML = createTreeText(obj);\n    }\n\n    function createTreeText(obj) { // standalone recursive function\n      let li = '';\n      let ul;\n      for (let key in obj) {\n        li += '<li>' + key + createTreeText(obj[key]) + '</li>';\n      }\n      if (li) {\n        ul = '<ul>' + li + '</ul>'\n      }\n      return ul || '';\n    }\n\n    createTree(container, data);\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/solution.md",
    "content": "The easiest way to walk the object is to use recursion.\n\n1. [The solution with innerHTML](sandbox:innerhtml).\n2. [The solution with DOM](sandbox:build-tree-dom).\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"tree\"></div>\n\n  <!-- The result should be:\n<div id=\"tree\">\n<ul>\n  <li>Fish\n    <ul>\n      <li>trout</li>\n      <li>salmon</li>\n    </ul>\n  </li>\n  <li>Trees\n    <ul>\n      <li>Huge\n        <ul>\n          <li>sequoia</li>\n          <li>oak</li>\n        </ul>\n      </li>\n      <li>Flowering\n        <ul>\n          <li>apple tree</li>\n          <li>magnolia</li>\n        </ul>\n      </li>\n    </ul>\n  </li>\n</ul>\n</div>\n-->\n\n  <script>\n    let data = {\n      \"Fish\": {\n        \"trout\": {},\n        \"salmon\": {}\n      },\n\n      \"Tree\": {\n        \"Huge\": {\n          \"sequoia\": {},\n          \"oak\": {}\n        },\n        \"Flowering\": {\n          \"apple tree\": {},\n          \"magnolia\": {}\n        }\n      }\n    };\n\n    function createTree(container, data) {\n      /* your code */\n    }\n\n    createTree(document.getElementById('tree'), data);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/7-create-object-tree/task.md",
    "content": "importance: 5\n\n---\n\n# Create a tree from the object\n\nWrite a function `createTree` that creates a nested `ul/li` list from the nested object.\n\nFor instance:\n\n```js\nlet data = {\n  \"Fish\": {\n    \"trout\": {},\n    \"salmon\": {}\n  },\n\n  \"Tree\": {\n    \"Huge\": {\n      \"sequoia\": {},\n      \"oak\": {}\n    },\n    \"Flowering\": {\n      \"apple tree\": {},\n      \"magnolia\": {}\n    }\n  }\n};\n```\n\nThe syntax:\n\n```js\nlet container = document.getElementById('container');\n*!*\ncreateTree(container, data); // creates the tree in the container\n*/!*\n```\n\nThe result (tree) should look like this:\n\n[iframe border=1 src=\"build-tree-dom\"]\n\nChoose one of two ways of solving this task:\n\n1. Create the HTML for the tree and then assign to `container.innerHTML`.\n2. Create tree nodes and append with DOM methods.\n\nWould be great if you could do both.\n\nP.S. The tree should not have \"extra\" elements like empty `<ul></ul>` for the leaves.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/solution.md",
    "content": "To append text to each `<li>` we can alter the text node `data`.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    let lis = document.getElementsByTagName('li');\n\n    for (let li of lis) {\n      // get the count of all <li> below this <li>\n      let descendantsCount = li.getElementsByTagName('li').length;\n      if (!descendantsCount) continue;\n\n      // add directly to the text node (append to the text)\n      li.firstChild.data += ' [' + descendantsCount + ']';\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <ul>\n    <li>Animals\n      <ul>\n        <li>Mammals\n          <ul>\n            <li>Cows</li>\n            <li>Donkeys</li>\n            <li>Dogs</li>\n            <li>Tigers</li>\n          </ul>\n        </li>\n        <li>Other\n          <ul>\n            <li>Snakes</li>\n            <li>Birds</li>\n            <li>Lizards</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Fishes\n      <ul>\n        <li>Aquarium\n          <ul>\n            <li>Guppy</li>\n            <li>Angelfish</li>\n          </ul>\n        </li>\n        <li>Sea\n          <ul>\n            <li>Sea trout</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // ... your code ...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/8-tree-count/task.md",
    "content": "importance: 5\n\n---\n\n# Show descendants in a tree\n\nThere's a tree organized as nested `ul/li`.\n\nWrite the code that adds to each `<li>` the number of its descendants. Skip leaves (nodes without children).\n\nThe result:\n\n[iframe border=1 src=\"solution\"]\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/solution.md",
    "content": "We'll create the table as a string: `\"<table>...</table>\"`, and then assign it to  `innerHTML`.\n\nThe algorithm:\n\n1. Create the table header with `<th>` and weekday names.\n2. Create the date object `d = new Date(year, month-1)`. That's the first day of `month` (taking into account that months in JavaScript start from `0`, not `1`).\n3. First few cells till the first day of the month `d.getDay()` may be empty. Let's fill them in with `<td></td>`.\n4. Increase the day in `d`: `d.setDate(d.getDate()+1)`. If `d.getMonth()` is not yet the next month, then add the new cell `<td>` to the calendar. If that's a Sunday, then add a newline <code>\"&lt;/tr&gt;&lt;tr&gt;\"</code>.\n5. If the month has finished, but the table row is not yet full, add empty `<td>` into it, to make it square.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td,\n    th {\n      border: 1px solid black;\n      padding: 3px;\n      text-align: center;\n    }\n\n    th {\n      font-weight: bold;\n      background-color: #E6E6E6;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"calendar\"></div>\n\n  <script>\n    function createCalendar(elem, year, month) {\n\n      let mon = month - 1; // months in JS are 0..11, not 1..12\n      let d = new Date(year, mon);\n\n      let table = '<table><tr><th>MO</th><th>TU</th><th>WE</th><th>TH</th><th>FR</th><th>SA</th><th>SU</th></tr><tr>';\n\n      // spaces for the first row\n      // from Monday till the first day of the month\n      // * * * 1  2  3  4\n      for (let i = 0; i < getDay(d); i++) {\n        table += '<td></td>';\n      }\n\n      // <td> with actual dates\n      while (d.getMonth() == mon) {\n        table += '<td>' + d.getDate() + '</td>';\n\n        if (getDay(d) % 7 == 6) { // sunday, last day of week - newline\n          table += '</tr><tr>';\n        }\n\n        d.setDate(d.getDate() + 1);\n      }\n\n      // add spaces after last days of month for the last row\n      // 29 30 31 * * * *\n      if (getDay(d) != 0) {\n        for (let i = getDay(d); i < 7; i++) {\n          table += '<td></td>';\n        }\n      }\n\n      // close the table\n      table += '</tr></table>';\n\n      elem.innerHTML = table;\n    }\n\n    function getDay(date) { // get day number from 0 (monday) to 6 (sunday)\n      let day = date.getDay();\n      if (day == 0) day = 7; // make Sunday (0) the last day\n      return day - 1;\n    }\n\n    createCalendar(calendar, 2012, 9);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    table {\n      border-collapse: collapse;\n    }\n\n    td,\n    th {\n      border: 1px solid black;\n      padding: 3px;\n      text-align: center;\n    }\n\n    th {\n      font-weight: bold;\n      background-color: #E6E6E6;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"calendar\"></div>\n\n  <script>\n    function createCalendar(elem, year, month) {\n      // ...your code that generates the calndar in elem...\n    }\n\n    createCalendar(calendar, 2012, 9);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/9-calendar-table/task.md",
    "content": "importance: 4\n\n---\n\n# Create a calendar\n\nWrite a function `createCalendar(elem, year, month)`.\n\nThe call should create a calendar for the given year/month and put it inside `elem`.\n\nThe calendar should be a table, where a week is `<tr>`, and a day is `<td>`. The table top should be `<th>` with weekday names: the first day should be Monday, and so on till Sunday.\n\nFor instance, `createCalendar(cal, 2012, 9)` should generate in element `cal` the following calendar:\n\n[iframe height=210 src=\"solution\"]\n\nP.S. For this task it's enough to generate the calendar, should not yet be clickable.\n"
  },
  {
    "path": "2-ui/1-document/07-modifying-document/article.md",
    "content": "# Modifying the document\n\nDOM modification is the key to creating \"live\" pages.\n\nHere we'll see how to create new elements \"on the fly\" and modify the existing page content.\n\n## Example: show a message\n\nLet's demonstrate using an example. We'll add a message on the page that looks nicer than `alert`.\n\nHere's how it will look:\n\n```html autorun height=\"80\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n*!*\n<div class=\"alert\">\n  <strong>Hi there!</strong> You've read an important message.\n</div>\n*/!*\n```\n\nThat was the HTML example. Now let's create the same `div` with JavaScript (assuming that the styles are in the HTML/CSS already).\n\n## Creating an element\n\nTo create DOM nodes, there are two methods:\n\n`document.createElement(tag)`\n: Creates a new *element node* with the given tag:\n\n    ```js\n    let div = document.createElement('div');\n    ```\n\n`document.createTextNode(text)`\n: Creates a new *text node* with the given text:\n\n    ```js\n    let textNode = document.createTextNode('Here I am');\n    ```\n\nMost of the time we need to create element nodes, such as the `div` for the message.\n\n### Creating the message\n\nCreating the message div takes 3 steps:\n\n```js\n// 1. Create <div> element\nlet div = document.createElement('div');\n\n// 2. Set its class to \"alert\"\ndiv.className = \"alert\";\n\n// 3. Fill it with the content\ndiv.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n```\n\nWe've created the element. But as of now it's only in a variable named `div`, not in the page yet. So we can't see it.\n\n## Insertion methods\n\nTo make the `div` show up, we need to insert it somewhere into `document`. For instance, into `<body>` element, referenced by `document.body`.\n\nThere's a special method `append` for that: `document.body.append(div)`.\n\nHere's the full code:\n\n```html run height=\"80\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  let div = document.createElement('div');\n  div.className = \"alert\";\n  div.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n\n*!*\n  document.body.append(div);\n*/!*\n</script>\n```\n\nHere we called `append` on `document.body`, but we can call `append` method on any other element, to put another element into it. For instance, we can append something to `<div>` by calling `div.append(anotherElement)`.\n\nHere are more insertion methods, they specify different places where to insert:\n\n- `node.append(...nodes or strings)` -- append nodes or strings *at the end* of `node`,\n- `node.prepend(...nodes or strings)` -- insert nodes or strings *at the beginning* of `node`,\n- `node.before(...nodes or strings)` –- insert nodes or strings *before* `node`,\n- `node.after(...nodes or strings)` –- insert nodes or strings *after* `node`,\n- `node.replaceWith(...nodes or strings)` –- replaces `node` with the given nodes or strings.\n\nArguments of these methods are an arbitrary list of DOM nodes to insert, or text strings (that become text nodes automatically).\n\nLet's see them in action.\n\nHere's an example of using these methods to add items to a list and the text before/after it:\n\n```html autorun\n<ol id=\"ol\">\n  <li>0</li>\n  <li>1</li>\n  <li>2</li>\n</ol>\n\n<script>\n  ol.before('before'); // insert string \"before\" before <ol>\n  ol.after('after'); // insert string \"after\" after <ol>\n\n  let liFirst = document.createElement('li');\n  liFirst.innerHTML = 'prepend';\n  ol.prepend(liFirst); // insert liFirst at the beginning of <ol>\n\n  let liLast = document.createElement('li');\n  liLast.innerHTML = 'append';\n  ol.append(liLast); // insert liLast at the end of <ol>\n</script>\n```\n\nHere's a visual picture of what the methods do:\n\n![](before-prepend-append-after.svg)\n\nSo the final list will be:\n\n```html\nbefore\n<ol id=\"ol\">\n  <li>prepend</li>\n  <li>0</li>\n  <li>1</li>\n  <li>2</li>\n  <li>append</li>\n</ol>\nafter\n```\n\nAs said, these methods can insert multiple nodes and text pieces in a single call.\n\nFor instance, here a string and an element are inserted:\n\n```html run\n<div id=\"div\"></div>\n<script>\n  div.before('<p>Hello</p>', document.createElement('hr'));\n</script>\n```\n\nPlease note: the text is inserted \"as text\", not \"as HTML\", with proper escaping of characters such as `<`, `>`.\n\nSo the final HTML is:\n\n```html run\n*!*\n&lt;p&gt;Hello&lt;/p&gt;\n*/!*\n<hr>\n<div id=\"div\"></div>\n```\n\nIn other words, strings are inserted in a safe way, like `elem.textContent` does it.\n\nSo, these methods can only be used to insert DOM nodes or text pieces.\n\nBut what if we'd like to insert an HTML string \"as html\", with all tags and stuff working, in the same manner as `elem.innerHTML` does it?\n\n## insertAdjacentHTML/Text/Element\n\nFor that we can use another, pretty versatile method: `elem.insertAdjacentHTML(where, html)`.\n\nThe first parameter is a code word, specifying where to insert relative to `elem`. Must be one of the following:\n\n- `\"beforebegin\"` -- insert `html` immediately before `elem`,\n- `\"afterbegin\"` -- insert `html` into `elem`, at the beginning,\n- `\"beforeend\"` -- insert `html` into `elem`, at the end,\n- `\"afterend\"` -- insert `html` immediately after `elem`.\n\nThe second parameter is an HTML string, that is inserted \"as HTML\".\n\nFor instance:\n\n```html run\n<div id=\"div\"></div>\n<script>\n  div.insertAdjacentHTML('beforebegin', '<p>Hello</p>');\n  div.insertAdjacentHTML('afterend', '<p>Bye</p>');\n</script>\n```\n\n...Would lead to:\n\n```html run\n<p>Hello</p>\n<div id=\"div\"></div>\n<p>Bye</p>\n```\n\nThat's how we can append arbitrary HTML to the page.\n\nHere's the picture of insertion variants:\n\n![](insert-adjacent.svg)\n\nWe can easily notice similarities between this and the previous picture. The insertion points are actually the same, but this method inserts HTML.\n\nThe method has two brothers:\n\n- `elem.insertAdjacentText(where, text)` -- the same syntax, but a string of `text` is inserted \"as text\" instead of HTML,\n- `elem.insertAdjacentElement(where, elem)` -- the same syntax, but inserts an element.\n\nThey exist mainly to make the syntax \"uniform\". In practice, only `insertAdjacentHTML` is used most of the time. Because for elements and text, we have methods `append/prepend/before/after` -- they are shorter to write and can insert nodes/text pieces.\n\nSo here's an alternative variant of showing a message:\n\n```html run\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  document.body.insertAdjacentHTML(\"afterbegin\", `<div class=\"alert\">\n    <strong>Hi there!</strong> You've read an important message.\n  </div>`);\n</script>\n```\n\n## Node removal\n\nTo remove a node, there's a method `node.remove()`.\n\nLet's make our message disappear after a second:\n\n```html run untrusted\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<script>\n  let div = document.createElement('div');\n  div.className = \"alert\";\n  div.innerHTML = \"<strong>Hi there!</strong> You've read an important message.\";\n\n  document.body.append(div);\n*!*\n  setTimeout(() => div.remove(), 1000);\n*/!*\n</script>\n```\n\nPlease note: if we want to *move* an element to another place -- there's no need to remove it from the old one.\n\n**All insertion methods automatically remove the node from the old place.**\n\nFor instance, let's swap elements:\n\n```html run height=50\n<div id=\"first\">First</div>\n<div id=\"second\">Second</div>\n<script>\n  // no need to call remove\n  second.after(first); // take #second and after it insert #first\n</script>\n```\n\n## Cloning nodes: cloneNode\n\nHow to insert one more similar message?\n\nWe could make a function and put the code there. But the alternative way would be to *clone* the existing `div` and modify the text inside it (if needed).\n\nSometimes when we have a big element, that may be faster and simpler.\n\n- The call `elem.cloneNode(true)` creates a \"deep\" clone of the element -- with all attributes and subelements. If we call `elem.cloneNode(false)`, then the clone is made without child elements.\n\nAn example of copying the message:\n\n```html run height=\"120\"\n<style>\n.alert {\n  padding: 15px;\n  border: 1px solid #d6e9c6;\n  border-radius: 4px;\n  color: #3c763d;\n  background-color: #dff0d8;\n}\n</style>\n\n<div class=\"alert\" id=\"div\">\n  <strong>Hi there!</strong> You've read an important message.\n</div>\n\n<script>\n*!*\n  let div2 = div.cloneNode(true); // clone the message\n  div2.querySelector('strong').innerHTML = 'Bye there!'; // change the clone\n\n  div.after(div2); // show the clone after the existing div\n*/!*\n</script>\n```\n\n## DocumentFragment [#document-fragment]\n\n`DocumentFragment` is a special DOM node that serves as a wrapper to pass around lists of nodes.\n\nWe can append other nodes to it, but when we insert it somewhere, then its content is inserted instead.\n\nFor example, `getListContent` below generates a fragment with `<li>` items, that are later inserted into `<ul>`:\n\n```html run\n<ul id=\"ul\"></ul>\n\n<script>\nfunction getListContent() {\n  let fragment = new DocumentFragment();\n\n  for(let i=1; i<=3; i++) {\n    let li = document.createElement('li');\n    li.append(i);\n    fragment.append(li);\n  }\n\n  return fragment;\n}\n\n*!*\nul.append(getListContent()); // (*)\n*/!*\n</script>\n```\n\nPlease note, at the last line `(*)` we append `DocumentFragment`, but it \"blends in\", so the resulting structure will be:\n\n```html\n<ul>\n  <li>1</li>\n  <li>2</li>\n  <li>3</li>\n</ul>\n```\n\n`DocumentFragment` is rarely used explicitly. Why append to a special kind of node, if we can return an array of nodes instead? Rewritten example:\n\n```html run\n<ul id=\"ul\"></ul>\n\n<script>\nfunction getListContent() {\n  let result = [];\n\n  for(let i=1; i<=3; i++) {\n    let li = document.createElement('li');\n    li.append(i);\n    result.push(li);\n  }\n\n  return result;\n}\n\n*!*\nul.append(...getListContent()); // append + \"...\" operator = friends!\n*/!*\n</script>\n```\n\nWe mention `DocumentFragment` mainly because there are some concepts on top of it, like [template](info:template-element) element, that we'll cover much later.\n\n## Old-school insert/remove methods\n\n[old]\n\nThere are also \"old school\" DOM manipulation methods, existing for historical reasons.\n\nThese methods come from really ancient times. Nowadays, there's no reason to use them, as modern methods, such as `append`, `prepend`, `before`, `after`, `remove`, `replaceWith`, are more flexible.\n\nThe only reason we list these methods here is that you can find them in many old scripts:\n\n`parentElem.appendChild(node)`\n: Appends `node` as the last child of `parentElem`.\n\n    The following example adds a new `<li>` to the end of `<ol>`:\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n\n    <script>\n      let newLi = document.createElement('li');\n      newLi.innerHTML = 'Hello, world!';\n\n      list.appendChild(newLi);\n    </script>\n    ```\n\n`parentElem.insertBefore(node, nextSibling)`\n: Inserts `node` before `nextSibling` into `parentElem`.\n\n    The following code inserts a new list item before the second `<li>`:\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n    <script>\n      let newLi = document.createElement('li');\n      newLi.innerHTML = 'Hello, world!';\n\n    *!*\n      list.insertBefore(newLi, list.children[1]);\n    */!*\n    </script>\n    ```\n    To insert `newLi` as the first element, we can do it like this:\n\n    ```js\n    list.insertBefore(newLi, list.firstChild);\n    ```\n\n`parentElem.replaceChild(node, oldChild)`\n: Replaces `oldChild` with `node` among children of `parentElem`.\n\n`parentElem.removeChild(node)`\n: Removes `node` from `parentElem` (assuming `node` is its child).\n\n    The following example removes first `<li>` from `<ol>`:\n\n    ```html run height=100\n    <ol id=\"list\">\n      <li>0</li>\n      <li>1</li>\n      <li>2</li>\n    </ol>\n\n    <script>\n      let li = list.firstElementChild;\n      list.removeChild(li);\n    </script>\n    ```\n\nAll these methods return the inserted/removed node. In other words, `parentElem.appendChild(node)` returns `node`. But usually the returned value is not used, we just run the method.\n\n## A word about \"document.write\"\n\nThere's one more, very ancient method of adding something to a web-page: `document.write`.\n\nThe syntax:\n\n```html run\n<p>Somewhere in the page...</p>\n*!*\n<script>\n  document.write('<b>Hello from JS</b>');\n</script>\n*/!*\n<p>The end</p>\n```\n\nThe call to `document.write(html)` writes the `html` into page \"right here and now\". The `html` string can be dynamically generated, so it's kind of flexible. We can use JavaScript to create a full-fledged webpage and write it.\n\nThe method comes from times when there was no DOM, no standards... Really old times. It still lives, because there are scripts using it.\n\nIn modern scripts we can rarely see it, because of the following important limitation:\n\n**The call to `document.write` only works while the page is loading.**\n\nIf we call it afterwards, the existing document content is erased.\n\nFor instance:\n\n```html run\n<p>After one second the contents of this page will be replaced...</p>\n*!*\n<script>\n  // document.write after 1 second\n  // that's after the page loaded, so it erases the existing content\n  setTimeout(() => document.write('<b>...By this.</b>'), 1000);\n</script>\n*/!*\n```\n\nSo it's kind of unusable at \"after loaded\" stage, unlike other DOM methods we covered above.\n\nThat's the downside.\n\nThere's an upside also. Technically, when `document.write` is called while the browser is reading (\"parsing\") incoming HTML, and it writes something, the browser consumes it just as if it were initially there, in the HTML text.\n\nSo it works blazingly fast, because there's *no DOM modification* involved. It writes directly into the page text, while the DOM is not yet built.\n\nSo if we need to add a lot of text into HTML dynamically, and we're at page loading phase, and the speed matters, it may help. But in practice these requirements rarely come together. And usually we can see this method in scripts just because they are old.\n\n## Summary\n\n- Methods to create new nodes:\n    - `document.createElement(tag)` -- creates an element with the given tag,\n    - `document.createTextNode(value)` -- creates a text node (rarely used),\n    - `elem.cloneNode(deep)` -- clones the element, if `deep==true` then with all descendants.  \n\n- Insertion and removal:\n    - `node.append(...nodes or strings)` -- insert into `node`, at the end,\n    - `node.prepend(...nodes or strings)` -- insert into `node`, at the beginning,\n    - `node.before(...nodes or strings)` –- insert right before `node`,\n    - `node.after(...nodes or strings)` –- insert right after `node`,\n    - `node.replaceWith(...nodes or strings)` –- replace `node`.\n    - `node.remove()` –- remove the `node`.\n\n    Text strings are inserted \"as text\".\n\n- There are also \"old school\" methods:\n    - `parent.appendChild(node)`\n    - `parent.insertBefore(node, nextSibling)`\n    - `parent.removeChild(node)`\n    - `parent.replaceChild(newElem, node)`\n\n    All these methods return `node`.\n\n- Given some HTML in `html`, `elem.insertAdjacentHTML(where, html)` inserts it depending on the value of `where`:\n    - `\"beforebegin\"` -- insert `html` right before `elem`,\n    - `\"afterbegin\"` -- insert `html` into `elem`, at the beginning,\n    - `\"beforeend\"` -- insert `html` into `elem`, at the end,\n    - `\"afterend\"` -- insert `html` right after `elem`.\n\n    Also there are similar methods, `elem.insertAdjacentText` and `elem.insertAdjacentElement`, that insert text strings and elements, but they are rarely used.\n\n- To append HTML to the page before it has finished loading:\n    - `document.write(html)`\n\n    After the page is loaded such a call erases the document. Mostly seen in old scripts.\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.css",
    "content": ".notification {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  font-size: 20px;\n  background: white;\n  text-align: center;\n}\n\n.welcome {\n  background: #b80000;\n  color: yellow;\n}\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <h2>Notification is on the right</h2>\n\n  <p>\n    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum\n    magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta\n    temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.\n  </p>\n\n  <script>\n    function showNotification({top = 0, right = 0, className, html}) {\n\n      let notification = document.createElement('div');\n      notification.className = \"notification\";\n      if (className) {\n        notification.classList.add(className);\n      }\n\n      notification.style.top = top + 'px';\n      notification.style.right = right + 'px';\n\n      notification.innerHTML = html;\n      document.body.append(notification);\n\n      setTimeout(() => notification.remove(), 1500);\n    }\n\n    // test it\n    let i = 1;\n    setInterval(() => {\n      showNotification({\n        top: 10,\n        right: 10,\n        html: 'Hello ' + i++,\n        className: \"welcome\"\n      });\n    }, 2000);\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.css",
    "content": ".notification {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  font-size: 20px;\n  background: white;\n  text-align: center;\n}\n\n.welcome {\n  background: #b80000;\n  color: yellow;\n}\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <h2>Notification is on the right</h2>\n\n  <p>\n    Lorem ipsum dolor sit amet, consectetur adipisicing elit. Dolorum aspernatur quam ex eaque inventore quod voluptatem adipisci omnis nemo nulla fugit iste numquam ducimus cumque minima porro ea quidem maxime necessitatibus beatae labore soluta voluptatum\n    magnam consequatur sit laboriosam velit excepturi laborum sequi eos placeat et quia deleniti? Corrupti velit impedit autem et obcaecati fuga debitis nemo ratione iste veniam amet dicta hic ipsam unde cupiditate incidunt aut iure ipsum officiis soluta\n    temporibus. Tempore dicta ullam delectus numquam consectetur quisquam explicabo culpa excepturi placeat quo sequi molestias reprehenderit hic at nemo cumque voluptates quidem repellendus maiores unde earum molestiae ad.\n  </p>\n\n  <script>\n    function showNotification({top = 0, right = 0, className, html}) {\n      /* your code */\n    }\n\n    // test it\n    let i = 1;\n    setInterval(() => {\n      showNotification({\n        top: 10,\n        right: 10,\n        html: 'Hello ' + i++,\n        className: \"welcome\"\n      });\n    }, 2000);\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/2-create-notification/task.md",
    "content": "importance: 5\n\n---\n\n# Create a notification\n\nWrite a function `showNotification(options)` that creates a notification: `<div class=\"notification\">` with the given content. The notification should automatically disappear after 1.5 seconds.\n\nThe options are:\n\n```js\n// shows an element with the text \"Hello\" near the right-top of the window\nshowNotification({\n  top: 10, // 10px from the top of the window (by default 0px)\n  right: 10, // 10px from the right edge of the window (by default 0px)\n  html: \"Hello!\", // the HTML of notification\n  className: \"welcome\" // an additional class for the div (optional)\n});\n```\n\n[demo src=\"solution\"]\n\n\nUse CSS positioning to show the element at given top/right coordinates. The source document has the necessary styles.\n"
  },
  {
    "path": "2-ui/1-document/08-styles-and-classes/article.md",
    "content": "# Styles and classes\n\nBefore we get into JavaScript's ways of dealing with styles and classes -- here's an important rule. Hopefully it's obvious enough, but we still have to mention it.\n\nThere are generally two ways to style an element:\n\n1. Create a class in CSS and add it: `<div class=\"...\">`\n2. Write properties directly into `style`: `<div style=\"...\">`.\n\nJavaScript can modify both classes and `style` properties.\n\nWe should always prefer CSS classes to `style`. The latter should only be used if classes \"can't handle it\".\n\nFor example, `style` is acceptable if we calculate coordinates of an element dynamically and want to set them from JavaScript, like this:\n\n```js\nlet top = /* complex calculations */;\nlet left = /* complex calculations */;\n\nelem.style.left = left; // e.g '123px', calculated at run-time\nelem.style.top = top; // e.g '456px'\n```\n\nFor other cases, like making the text red, adding a background icon -- describe that in CSS and then add the class (JavaScript can do that). That's more flexible and easier to support.\n\n## className and classList\n\nChanging a class is one of the most often used actions in scripts.\n\nIn the ancient time, there was a limitation in JavaScript: a reserved word like `\"class\"` could not be an object property. That limitation does not exist now, but at that time it was impossible to have a `\"class\"` property, like `elem.class`.\n\nSo for classes the similar-looking property `\"className\"` was introduced: the `elem.className` corresponds to the `\"class\"` attribute.\n\nFor instance:\n\n```html run\n<body class=\"main page\">\n  <script>\n    alert(document.body.className); // main page\n  </script>\n</body>\n```\n\nIf we assign something to `elem.className`, it replaces the whole string of classes. Sometimes that's what we need, but often we want to add/remove a single class.\n\nThere's another property for that: `elem.classList`.\n\nThe `elem.classList` is a special object with methods to `add/remove/toggle` a single class.\n\nFor instance:\n\n```html run\n<body class=\"main page\">\n  <script>\n*!*\n    // add a class\n    document.body.classList.add('article');\n*/!*\n\n    alert(document.body.className); // main page article\n  </script>\n</body>\n```\n\nSo we can operate both on the full class string using `className` or on individual classes using `classList`. What we choose depends on our needs.\n\nMethods of `classList`:\n\n- `elem.classList.add/remove(\"class\")` -- adds/removes the class.\n- `elem.classList.toggle(\"class\")` -- adds the class if it doesn't exist, otherwise removes it.\n- `elem.classList.contains(\"class\")` -- checks for the given class, returns `true/false`.\n\nBesides, `classList` is iterable, so we can list all classes with `for..of`, like this:\n\n```html run\n<body class=\"main page\">\n  <script>\n    for (let name of document.body.classList) {\n      alert(name); // main, and then page\n    }\n  </script>\n</body>\n```\n\n## Element style\n\nThe property `elem.style` is an object that corresponds to what's written in the `\"style\"` attribute. Setting `elem.style.width=\"100px\"` works the same as if we had in the attribute `style` a string `width:100px`.\n\nFor multi-word property the camelCase is used:\n\n```js no-beautify\nbackground-color  => elem.style.backgroundColor\nz-index           => elem.style.zIndex\nborder-left-width => elem.style.borderLeftWidth\n```\n\nFor instance:\n\n```js run\ndocument.body.style.backgroundColor = prompt('background color?', 'green');\n```\n\n````smart header=\"Prefixed properties\"\nBrowser-prefixed properties like `-moz-border-radius`, `-webkit-border-radius` also follow the same rule: a dash means upper case.\n\nFor instance:\n\n```js\nbutton.style.MozBorderRadius = '5px';\nbutton.style.WebkitBorderRadius = '5px';\n```\n````\n\n## Resetting the style property\n\nSometimes we want to assign a style property, and later remove it.\n\nFor instance, to hide an element, we can set `elem.style.display = \"none\"`.\n\nThen later we may want to remove the `style.display` as if it were not set. Instead of `delete elem.style.display` we should assign an empty string to it: `elem.style.display = \"\"`.\n\n```js run\n// if we run this code, the <body> will blink\ndocument.body.style.display = \"none\"; // hide\n\nsetTimeout(() => document.body.style.display = \"\", 1000); // back to normal\n```\n\nIf we set `style.display` to an empty string, then the browser applies CSS classes and its built-in styles normally, as if there were no such `style.display` property at all.\n\n````smart header=\"Full rewrite with `style.cssText`\"\nNormally, we use `style.*` to assign individual style properties. We can't set the full style like `div.style=\"color: red; width: 100px\"`, because `div.style` is an object, and it's read-only.\n\nTo set the full style as a string, there's a special property `style.cssText`:\n\n```html run\n<div id=\"div\">Button</div>\n\n<script>\n  // we can set special style flags like \"important\" here\n  div.style.cssText=`color: red !important;\n    background-color: yellow;\n    width: 100px;\n    text-align: center;\n  `;\n\n  alert(div.style.cssText);\n</script>\n```\n\nThis property is rarely used, because such assignment removes all existing styles: it does not add, but replaces them. May occasionally delete something needed. But we can safely use it for new elements, when we know we won't delete an existing style.\n\nThe same can be accomplished by setting an attribute: `div.setAttribute('style', 'color: red...')`.\n````\n\n## Mind the units\n\nDon't forget to add CSS units to values.\n\nFor instance, we should not set `elem.style.top` to `10`, but rather to `10px`. Otherwise it wouldn't work:\n\n```html run height=100\n<body>\n  <script>\n  *!*\n    // doesn't work!\n    document.body.style.margin = 20;\n    alert(document.body.style.margin); // '' (empty string, the assignment is ignored)\n  */!*\n\n    // now add the CSS unit (px) - and it works\n    document.body.style.margin = '20px';\n    alert(document.body.style.margin); // 20px\n\n    alert(document.body.style.marginTop); // 20px\n    alert(document.body.style.marginLeft); // 20px\n  </script>\n</body>\n```\n\nPlease note: the browser \"unpacks\" the property `style.margin` in the last lines and infers `style.marginLeft` and `style.marginTop` from it.\n\n## Computed styles: getComputedStyle\n\nSo, modifying a style is easy. But how to *read* it?\n\nFor instance, we want to know the size, margins, the color of an element. How to do it?\n\n**The `style` property operates only on the value of the `\"style\"` attribute, without any CSS cascade.**\n\nSo we can't read anything that comes from CSS classes using `elem.style`.\n\nFor instance, here `style` doesn't see the margin:\n\n```html run height=60 no-beautify\n<head>\n  <style> body { color: red; margin: 5px } </style>\n</head>\n<body>\n\n  The red text\n  <script>\n*!*\n    alert(document.body.style.color); // empty\n    alert(document.body.style.marginTop); // empty\n*/!*\n  </script>\n</body>\n```\n\n...But what if we need, say, to increase the margin by `20px`? We would want the current value of it.\n\nThere's another method for that: `getComputedStyle`.\n\nThe syntax is:\n\n```js\ngetComputedStyle(element, [pseudo])\n```\n\nelement\n: Element to read the value for.\n\npseudo\n: A pseudo-element if required, for instance `::before`. An empty string or no argument means the element itself.\n\nThe result is an object with styles, like `elem.style`, but now with respect to all CSS classes.\n\nFor instance:\n\n```html run height=100\n<head>\n  <style> body { color: red; margin: 5px } </style>\n</head>\n<body>\n\n  <script>\n    let computedStyle = getComputedStyle(document.body);\n\n    // now we can read the margin and the color from it\n\n    alert( computedStyle.marginTop ); // 5px\n    alert( computedStyle.color ); // rgb(255, 0, 0)\n  </script>\n\n</body>\n```\n\n```smart header=\"Computed and resolved values\"\nThere are two concepts in [CSS](https://drafts.csswg.org/cssom/#resolved-values):\n\n1. A *computed* style value is the value after all CSS rules and CSS inheritance is applied, as the result of the CSS cascade. It can look like `height:1em` or `font-size:125%`.\n2. A *resolved* style value is the one finally applied to the element. Values like `1em` or `125%` are relative. The browser takes the computed value and makes all units fixed and absolute, for instance: `height:20px` or `font-size:16px`. For geometry properties resolved values may have a floating point, like `width:50.5px`.\n\nA long time ago `getComputedStyle` was created to get computed values, but it turned out that resolved values are much more convenient, and the standard changed.\n\nSo nowadays `getComputedStyle` actually returns the resolved value of the property, usually in `px` for geometry.\n```\n\n````warn header=\"`getComputedStyle` requires the full property name\"\nWe should always ask for the exact property that we want, like `paddingLeft` or `marginTop` or `borderTopWidth`. Otherwise the correct result is not guaranteed.\n\nFor instance, if there are properties `paddingLeft/paddingTop`, then what should we get for `getComputedStyle(elem).padding`? Nothing, or maybe a \"generated\" value from known paddings? There's no standard rule here.\n\nThere are other inconsistencies. As an example, some browsers (Chrome) show `10px` in the document below, and some of them (Firefox) --  do not:\n\n```html run\n<style>\n  body {\n    margin: 10px;\n  }\n</style>\n<script>\n  let style = getComputedStyle(document.body);\n  alert(style.margin); // empty string in Firefox\n</script>\n```\n````\n\n```smart header=\"Styles applied to `:visited` links are hidden!\"\nVisited links may be colored using `:visited` CSS pseudoclass.\n\nBut `getComputedStyle` does not give access to that color, because otherwise an arbitrary page could find out whether the user visited a link by creating it on the page and checking the styles.\n\nJavaScript may not see the styles applied by `:visited`. And also, there's a limitation in CSS that forbids applying geometry-changing styles in `:visited`. That's to guarantee that there's no side way for an evil page to test if a link was visited and hence to break the privacy.\n```\n\n## Summary\n\nTo manage classes, there are two DOM properties:\n\n- `className` -- the string value, good to manage the whole set of classes.\n- `classList` -- the object with methods `add/remove/toggle/contains`, good for individual classes.\n\nTo change the styles:\n\n- The `style` property is an object with camelCased styles. Reading and writing to it has the same meaning as modifying individual properties in the `\"style\"` attribute. To see how to apply `important` and other rare stuff -- there's a list of methods at [MDN](mdn:api/CSSStyleDeclaration).\n\n- The `style.cssText` property corresponds to the whole `\"style\"` attribute, the full string of styles.\n\nTo read the resolved styles (with respect to all classes, after all CSS is applied and final values are calculated):\n\n- The `getComputedStyle(elem, [pseudo])` returns the style-like object with them. Read-only.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/solution.md",
    "content": "The solution is:\n\n```js\nlet scrollBottom = elem.scrollHeight - elem.scrollTop - elem.clientHeight;\n```\n\nIn other words: (full height) minus (scrolled out top part) minus (visible part) -- that's exactly the scrolled out bottom part.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/1-get-scroll-height-bottom/task.md",
    "content": "importance: 5\n\n---\n\n# What's the scroll from the bottom?\n\nThe `elem.scrollTop` property is the size of the scrolled out part from the top. How to get the size of the bottom scroll (let's call it `scrollBottom`)?\n\nWrite the code that works for an arbitrary `elem`.\n\nP.S. Please check your code: if there's no scroll or the element is fully scrolled down, then it should return `0`.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/2-scrollbar-width/solution.md",
    "content": "To get the scrollbar width, we can create an element with the scroll, but without borders and paddings.\n\nThen the difference between its full width `offsetWidth` and the inner content area width `clientWidth` will be exactly the scrollbar:\n\n```js run\n// create a div with the scroll\nlet div = document.createElement('div');\n\ndiv.style.overflowY = 'scroll';\ndiv.style.width = '50px';\ndiv.style.height = '50px';\n\n// must put it in the document, otherwise sizes will be 0\ndocument.body.append(div);\nlet scrollWidth = div.offsetWidth - div.clientWidth;\n\ndiv.remove();\n\nalert(scrollWidth);\n```\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/2-scrollbar-width/task.md",
    "content": "importance: 3\n\n---\n\n# What is the scrollbar width?\n\nWrite the code that returns the width of a standard scrollbar.\n\nFor Windows it usually varies between `12px` and `20px`. If the browser doesn't reserve any space for it (the scrollbar is half-translucent over the text, also happens), then it may be `0px`.\n\nP.S. The code should work for any HTML document, do not depend on its content.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/ball-half/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" height=\"40\" width=\"40\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <script>\n    let ball = document.getElementById('ball')\n    let field = document.getElementById('field')\n\n    // ball.offsetWidth=0 before image loaded!\n    // to fix: set width\n    ball.style.left = Math.round(field.clientWidth / 2) + 'px'\n    ball.style.top = Math.round(field.clientHeight / 2) + 'px'\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.md",
    "content": "The ball has `position:absolute`. It means that its `left/top` coordinates are measured from the nearest positioned element, that is `#field` (because it has `position:relative`).\n\nThe coordinates start from the inner left-upper corner of the field:\n\n![](field.svg)\n\nThe inner field width/height is `clientWidth/clientHeight`. So the field center has coordinates `(clientWidth/2, clientHeight/2)`.\n\n...But if we set `ball.style.left/top` to such values, then not the ball as a whole, but the left-upper edge of the ball would be in the center:\n\n```js\nball.style.left = Math.round(field.clientWidth / 2) + 'px';\nball.style.top = Math.round(field.clientHeight / 2) + 'px';\n```\n\nHere's how it looks:\n\n[iframe height=180 src=\"ball-half\"]\n\nTo align the ball center with the center of the field, we should move the ball to the half of its width to the left and to the half of its height to the top:\n\n```js\nball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px';\nball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px';\n```\n\nNow the ball is finally centered.\n\n````warn header=\"Attention: the pitfall!\"\n\nThe code won't work reliably while `<img>` has no width/height:\n\n```html\n<img src=\"ball.png\" id=\"ball\">\n```\n````\n\nWhen the browser does not know the width/height of an image (from tag attributes or CSS), then it assumes them to equal `0` until the image finishes loading.\n\nSo the value of `ball.offsetWidth` will be `0` until the image loads. That leads to wrong coordinates in the code above.\n\nAfter the first load, the browser usually caches the image, and on reloads it will have the size immediately. But on the first load the value of `ball.offsetWidth` is `0`.\n\nWe should fix that by adding `width/height` to `<img>`:\n\n```html\n<img src=\"ball.png\" *!*width=\"40\" height=\"40\"*/!* id=\"ball\">\n```\n\n...Or provide the size in CSS:\n\n```css\n#ball {\n  width: 40px;\n  height: 40px;\n}\n```\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <script>\n    // ball.offsetWidth=0 before image loaded!\n    // to fix: set width\n    ball.style.left = Math.round(field.clientWidth / 2 - ball.offsetWidth / 2) + 'px'\n    ball.style.top = Math.round(field.clientHeight / 2 - ball.offsetHeight / 2) + 'px'\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #field {\n      width: 200px;\n      border: 10px groove black;\n      background-color: #00FF00;\n      position: relative;\n    }\n\n    #ball {\n      position: absolute;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/4-put-ball-in-center/task.md",
    "content": "importance: 5\n\n---\n\n# Place the ball in the field center\n\nHere's how the source document looks:\n\n[iframe src=\"source\" edit link height=180]\n\nWhat are coordinates of the field center?\n\nCalculate them and use to place the ball into the center of the green field:\n\n[iframe src=\"solution\" height=180]\n\n- The element should be moved by JavaScript, not CSS.\n- The code should work with any ball size (`10`, `20`, `30` pixels) and any field size, not be bound to the given values.\n\nP.S. Sure, centering could be done with CSS, but here we want exactly JavaScript. Further we'll meet other topics and more complex situations when JavaScript must be used. Here we do a \"warm-up\".\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/solution.md",
    "content": "Differences:\n\n1. `clientWidth` is numeric, while `getComputedStyle(elem).width` returns a string with `px` at the end.\n2. `getComputedStyle` may return non-numeric width like `\"auto\"` for an inline element.\n3. `clientWidth` is the inner content area of the element plus paddings, while CSS width (with standard `box-sizing`) is the inner content area *without paddings*.\n4. If there's a scrollbar and the browser reserves the space for it, some browser substract that space from CSS width (cause it's not available for content any more), and some do not. The `clientWidth` property is always the same: scrollbar size is substracted if reserved.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/6-width-vs-clientwidth/task.md",
    "content": "importance: 5\n\n---\n\n# The difference: CSS width versus clientWidth\n\nWhat's the difference between `getComputedStyle(elem).width` and `elem.clientWidth`?\n\nGive at least 3 differences. The more the better.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/article.md",
    "content": "# Element size and scrolling\n\nThere are many JavaScript properties that allow us to read information about element width, height and other geometry features.\n\nWe often need them when moving or positioning elements in JavaScript.\n\n## Sample element\n\nAs a sample element to demonstrate properties we'll use the one given below:\n\n```html no-beautify\n<div id=\"example\">\n  ...Text...\n</div>\n<style>\n  #example {\n    width: 300px;\n    height: 200px;\n    border: 25px solid #E8C48F;\n    padding: 20px;              \n    overflow: auto;             \n  }\n</style>\n```\n\nIt has the border, padding and scrolling. The full set of features. There are no margins, as they are not the part of the element itself, and there are no special properties for them.\n\nThe element looks like this:\n\n![](metric-css.svg)\n\nYou can [open the document in the sandbox](sandbox:metric).\n\n```smart header=\"Mind the scrollbar\"\nThe picture above demonstrates the most complex case when the element has a scrollbar. Some browsers (not all) reserve the space for it by taking it from the content (labeled as \"content width\" above).\n\nSo, without scrollbar the content width would be `300px`, but if the scrollbar is `16px` wide (the width may vary between devices and browsers) then only `300 - 16 = 284px` remains, and we should take it into account. That's why examples from this chapter assume that there's a scrollbar. Without it, some calculations are simpler.\n```\n\n```smart header=\"The `padding-bottom` area may be filled with text\"\nUsually paddings are shown empty on our illustrations, but if there's a lot of text in the element and it overflows, then browsers show the \"overflowing\" text at `padding-bottom`, that's normal.\n```\n\n## Geometry\n\nHere's the overall picture with geometry properties:\n\n![](metric-all.svg)\n\nValues of these properties are technically numbers, but these numbers are \"of pixels\", so these are pixel measurements.\n\nLet's start exploring the properties starting from the outside of the element.\n\n## offsetParent, offsetLeft/Top\n\nThese properties are rarely needed, but still they are the \"most outer\" geometry properties, so we'll start with them.\n\nThe `offsetParent` is the nearest ancestor that the browser uses for calculating coordinates during rendering.\n\nThat's the nearest ancestor that is one of the following:\n\n1. CSS-positioned (`position` is `absolute`, `relative`, `fixed` or `sticky`),  or\n2. `<td>`, `<th>`, or `<table>`,  or\n3. `<body>`.\n\nProperties `offsetLeft/offsetTop` provide x/y coordinates relative to `offsetParent` upper-left corner.\n\nIn the example below the inner `<div>` has `<main>` as `offsetParent` and `offsetLeft/offsetTop` shifts from its upper-left corner (`180`):\n\n```html run height=10\n<main style=\"position: relative\" id=\"main\">\n  <article>\n    <div id=\"example\" style=\"position: absolute; left: 180px; top: 180px\">...</div>\n  </article>\n</main>\n<script>\n  alert(example.offsetParent.id); // main\n  alert(example.offsetLeft); // 180 (note: a number, not a string \"180px\")\n  alert(example.offsetTop); // 180\n</script>\n```\n\n![](metric-offset-parent.svg)\n\nThere are several occasions when `offsetParent` is `null`:\n\n1. For not shown elements (`display:none` or not in the document).\n2. For `<body>` and `<html>`.\n3. For elements with `position:fixed`.\n\n## offsetWidth/Height\n\nNow let's move on to the element itself.\n\nThese two properties are the simplest ones. They provide the \"outer\" width/height of the element. Or, in other words, its full size including borders.\n\n![](metric-offset-width-height.svg)\n\nFor our sample element:\n\n- `offsetWidth = 390` -- the outer width, can be calculated as inner CSS-width (`300px`) plus paddings (`2 * 20px`) and borders (`2 * 25px`).\n- `offsetHeight = 290` -- the outer height.\n\n````smart header=\"Geometry properties are zero/null for elements that are not displayed\"\nGeometry properties are calculated only for displayed elements.\n\nIf an element (or any of its ancestors) has `display:none` or is not in the document, then all geometry properties are zero (or `null` for `offsetParent`).\n\nFor example, `offsetParent` is `null`, and `offsetWidth`, `offsetHeight` are `0` when we created an element, but haven't inserted it into the document yet, or it (or it's ancestor) has `display:none`.\n\nWe can use this to check if an element is hidden, like this:\n\n```js\nfunction isHidden(elem) {\n  return !elem.offsetWidth && !elem.offsetHeight;\n}\n```\n\nPlease note that such `isHidden` returns `true` for elements that are on-screen, but have zero sizes (like an empty `<div>`).\n````\n\n## clientTop/Left\n\nInside the element we have the borders.\n\nTo measure them, there are properties `clientTop` and `clientLeft`.\n\nIn our example:\n\n- `clientLeft = 25` -- left border width\n- `clientTop = 25` -- top border width\n\n![](metric-client-left-top.svg)\n\n...But to be precise -- these properties are not border width/height, but rather relative coordinates of the inner side from the outer side.\n\nWhat's the difference?\n\nIt becomes visible when the document is right-to-left (the operating system is in Arabic or Hebrew languages). The scrollbar is then not on the right, but on the left, and then `clientLeft` also includes the scrollbar width.\n\nIn that case, `clientLeft` would be not `25`, but with the scrollbar width `25 + 16 = 41`.\n\nHere's the example in hebrew:\n\n![](metric-client-left-top-rtl.svg)\n\n## clientWidth/Height\n\nThese properties provide the size of the area inside the element borders.\n\nThey include the content width together with paddings, but without the scrollbar:\n\n![](metric-client-width-height.svg)\n\nOn the picture above let's first consider `clientHeight`.\n\nThere's no horizontal scrollbar, so it's exactly the sum of what's inside the borders: CSS-height `200px` plus top and bottom paddings (`2 * 20px`) total `240px`.\n\nNow `clientWidth` -- here the content width is not `300px`, but `284px`, because `16px` are occupied by the scrollbar. So the sum is `284px` plus left and right paddings, total `324px`.\n\n**If there are no paddings, then `clientWidth/Height` is exactly the content area, inside the borders and the scrollbar (if any).**\n\n![](metric-client-width-nopadding.svg)\n\nSo when there's no padding we can use `clientWidth/clientHeight` to get the content area size.\n\n## scrollWidth/Height\n\nThese properties are like `clientWidth/clientHeight`, but they also include the scrolled out (hidden) parts:\n\n![](metric-scroll-width-height.svg)\n\nOn the picture above:\n\n- `scrollHeight = 723` -- is the full inner height of the content area including the scrolled out parts.\n- `scrollWidth = 324` -- is the full inner width, here we have no horizontal scroll, so it equals `clientWidth`.\n\nWe can use these properties to expand the element wide to its full width/height.\n\nLike this:\n\n```js\n// expand the element to the full content height\nelement.style.height = `${element.scrollHeight}px`;\n```\n\n```online\nClick the button to expand the element:\n\n<div id=\"element\" style=\"width:300px;height:200px; padding: 0;overflow: auto; border:1px solid black;\">text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text</div>\n\n<button style=\"padding:0\" onclick=\"element.style.height = `${element.scrollHeight}px`\">element.style.height = `${element.scrollHeight}px`</button>\n```\n\n## scrollLeft/scrollTop\n\nProperties `scrollLeft/scrollTop` are the width/height of the hidden, scrolled out part of the element.\n\nOn the picture below we can see `scrollHeight` and `scrollTop` for a block with a vertical scroll.\n\n![](metric-scroll-top.svg)\n\nIn other words, `scrollTop` is \"how much is scrolled up\".\n\n````smart header=\"`scrollLeft/scrollTop` can be modified\"\nMost of the geometry properties here are read-only, but `scrollLeft/scrollTop` can be changed, and the browser will scroll the element.\n\n```online\nIf you click the element below, the code `elem.scrollTop += 10` executes. That makes the element content scroll `10px` down.\n\n<div onclick=\"this.scrollTop+=10\" style=\"cursor:pointer;border:1px solid black;width:100px;height:80px;overflow:auto\">Click<br>Me<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br>8<br>9</div>\n```\n\nSetting `scrollTop` to `0` or a big value, such as `1e9` will make the element scroll to the very top/bottom respectively.\n````\n\n## Don't take width/height from CSS\n\nWe've just covered geometry properties of DOM elements, that can be used to get widths, heights and calculate distances.\n\nBut as we know from the chapter <info:styles-and-classes>, we can read CSS-height and width using `getComputedStyle`.\n\nSo why not to read the width of an element with `getComputedStyle`, like this?\n\n```js run\nlet elem = document.body;\n\nalert( getComputedStyle(elem).width ); // show CSS width for elem\n```\n\nWhy should we use geometry properties instead? There are two reasons:\n\n1. First, CSS `width/height` depend on another property: `box-sizing` that defines \"what is\" CSS width and height. A change in `box-sizing` for CSS purposes may break such JavaScript.\n2. Second, CSS `width/height` may be `auto`, for instance for an inline element:\n\n    ```html run\n    <span id=\"elem\">Hello!</span>\n\n    <script>\n    *!*\n      alert( getComputedStyle(elem).width ); // auto\n    */!*\n    </script>\n    ```\n\n    From the CSS standpoint, `width:auto` is perfectly normal, but in JavaScript we need an exact size in `px` that we can use in calculations. So here CSS width is useless.\n\nAnd there's one more reason: a scrollbar. Sometimes the code that works fine without a scrollbar becomes buggy with it, because a scrollbar takes the space from the content in some browsers. So the real width available for the content is *less* than CSS width. And `clientWidth/clientHeight` take that into account.\n\n...But with `getComputedStyle(elem).width` the situation is different. Some browsers (e.g. Chrome) return the real inner width, minus the scrollbar, and some of them (e.g. Firefox) -- CSS width (ignore the scrollbar). Such cross-browser differences is the reason not to use `getComputedStyle`, but rather rely on geometry properties.\n\n```online\nIf your browser reserves the space for a scrollbar (most browsers for Windows do), then you can test it below.\n\n[iframe src=\"cssWidthScroll\" link border=1]\n\nThe element with text has CSS `width:300px`.\n\nOn a Desktop Windows OS, Firefox, Chrome, Edge all reserve the space for the scrollbar. But  Firefox shows `300px`, while Chrome and Edge show less. That's because Firefox returns the CSS width and other browsers return the \"real\" width.\n```\n\nPlease note that the described difference is only about reading `getComputedStyle(...).width` from JavaScript, visually everything is correct.\n\n## Summary\n\nElements have the following geometry properties:\n\n- `offsetParent` -- is the nearest positioned ancestor or `td`, `th`, `table`, `body`.\n- `offsetLeft/offsetTop` -- coordinates relative to the upper-left edge of `offsetParent`.\n- `offsetWidth/offsetHeight` -- \"outer\" width/height of an element including borders.\n- `clientLeft/clientTop` -- the distances from the upper-left outer corner to the upper-left inner (content + padding) corner. For left-to-right OS they are always the widths of left/top borders. For right-to-left OS the vertical scrollbar is on the left so `clientLeft` includes its width too.\n- `clientWidth/clientHeight` -- the width/height of the content including paddings, but without the scrollbar.\n- `scrollWidth/scrollHeight` -- the width/height of the content, just like `clientWidth/clientHeight`, but also include scrolled-out, invisible part of the element.\n- `scrollLeft/scrollTop` -- width/height of the scrolled out upper part of the element, starting from its upper-left corner.\n\nAll properties are read-only except `scrollLeft/scrollTop` that make the browser scroll the element if changed.\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/cssWidthScroll.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n  <div id=\"elem\" style=\"overflow-y:scroll;width:300px;height:200px;border:1px solid black\">\n    text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text\n    text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text text\n    text text text text text text text text text text text text text text text text text text text text text text text text text text\n  </div>\n\n  The element has <code>style=\"width:300px\"</code>\n  <br>\n\n  <button onclick=\"alert( getComputedStyle(elem).width )\">alert( getComputedStyle(elem).width )</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/09-size-and-scroll/metric.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style type=\"text/css\">\n    * {\n      margin: 0;\n      padding: 0;\n    }\n\n    #example {\n      width: 300px;\n      height: 200px;\n      overflow: auto;\n      border: 25px solid #E8C48F;\n      padding: 20px;\n    }\n\n    .key {\n      cursor: pointer;\n      text-decoration: underline;\n    }\n  </style>\n\n</head>\n\n<body>\n\n\n  <div id=\"example\">\n    <h3>Introduction</h3>\n    <p>This Ecma Standard is based on several originating technologies, the most well known being JavaScript (Netscape) and JScript (Microsoft). The language was invented by Brendan Eich at Netscape and first appeared in that company's Navigator 2.0 browser.\n      It has appeared in all subsequent browsers from Netscape and in all browsers from Microsoft starting with Internet Explorer 3.0. The development of this Standard started in November 1996. The first edition of this Ecma Standard was adopted by the\n      Ecma General Assembly of June 1997.</p>\n\n    <p>That Ecma Standard was submitted to ISO/IEC JTC 1 for adoption under the fast-track procedure, and approved as international standard ISO/IEC 16262, in April 1998. The Ecma General Assembly of June 1998 approved the second edition of ECMA-262 to keep\n      it fully aligned with ISO/IEC 16262. Changes between the first and the second edition are editorial in nature.</p>\n\n    <p>The third edition of the Standard introduced powerful regular expressions, better string handling, new control statements, try/catch exception handling, tighter definition of errors, formatting for numeric output and minor changes in anticipation\n      of forthcoming internationalisation facilities and future language growth. The third edition of the ECMAScript standard was adopted by the Ecma General Assembly of December 1999 and published as ISO/IEC 16262:2002 in June 2002.</p>\n\n  </div>\n\n\n  <div id=\"mouse-wrap\">Mouse coordinates: <span id=\"mouse\">...</span></div>\n  <div id=\"info\"></div>\n\n\n  <script>\n    let props = {\n      geometry: ['clientLeft', 'clientTop', 'clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight', 'scrollWidth', 'scrollHeight'],\n      scroll: ['scrollLeft', 'scrollTop'],\n      offsetParent: ['offsetParent', 'offsetLeft', 'offsetTop']\n    };\n\n    info.innerHTML = '<h3>Click to see the value:</h3>';\n    for (let k in props) {\n      info.innerHTML += `<h4>${k}</h4>`;\n      let prop = props[k];\n      for (let i = 0; i < prop.length; i++) {\n        info.innerHTML += \"<span class='key'>\" + prop[i] + '</span>: <span id=\"' + prop[i] + '\">&nbsp;</span>' + \" \"\n        i++;\n        if (i < prop.length) {\n          info.innerHTML += \"<span class='key'>\" + prop[i] + '</span>: <span id=\"' + prop[i] + '\">&nbsp;</span>';\n        }\n        info.innerHTML += \"<br/>\";\n\n      }\n    }\n\n    document.onclick = function(event) {\n      let target = event.target;\n      if (!target.classList.contains('key')) return;\n\n      let prop = target.innerHTML;\n      let value = example[prop];\n      value = value.tagName || value;\n      document.getElementById(prop).innerHTML = value;\n    };\n\n\n    document.onmousemove = function(e) {\n      document.getElementById('mouse').innerHTML = Math.round(e.clientX) + ':' + Math.round(e.clientY);\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/10-size-and-scroll-window/article.md",
    "content": "# Window sizes and scrolling\n\nHow do we find the width and height of the browser window? How do we get the full width and height of the document, including the scrolled out part? How do we scroll the page using JavaScript?\n\nFor this type of information, we can use the root document element `document.documentElement`, that corresponds to the `<html>` tag. But there are additional methods and peculiarities to consider.\n\n## Width/height of the window\n\nTo get window width and height, we can use the `clientWidth/clientHeight` of `document.documentElement`:\n\n![](document-client-width-height.svg)\n\n```online\nFor instance, this button shows the height of your window:\n\n<button onclick=\"alert(document.documentElement.clientHeight)\">alert(document.documentElement.clientHeight)</button>\n```\n\n````warn header=\"Not `window.innerWidth/innerHeight`\"\nBrowsers also support properties like `window.innerWidth/innerHeight`. They look like what we want, so why not to use them instead?\n\nIf there exists a scrollbar, and it occupies some space, `clientWidth/clientHeight` provide the width/height without it (subtract it). In other words, they return the width/height of the visible part of the document, available for the content.\n\n`window.innerWidth/innerHeight` includes the scrollbar.\n\nIf there's a scrollbar, and it occupies some space, then these two lines show different values:\n```js run\nalert( window.innerWidth ); // full window width\nalert( document.documentElement.clientWidth ); // window width minus the scrollbar\n```\n\nIn most cases, we need the *available* window width in order to draw or position something within scrollbars (if there are any), so we should use `documentElement.clientHeight/clientWidth`.\n````\n\n```warn header=\"`DOCTYPE` is important\"\nPlease note: top-level geometry properties may work a little bit differently when there's no `<!DOCTYPE HTML>` in HTML. Odd things are possible.\n\nIn modern HTML we should always write `DOCTYPE`.\n```\n\n## Width/height of the document\n\nTheoretically, as the root document element is `document.documentElement`, and it encloses all the content, we could measure the document's full size as `document.documentElement.scrollWidth/scrollHeight`.\n\nBut on that element, for the whole page, these properties do not work as intended. In Chrome/Safari/Opera, if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! Weird, right?\n\nTo reliably obtain the full document height, we should take the maximum of these properties:\n\n```js run\nlet scrollHeight = Math.max(\n  document.body.scrollHeight, document.documentElement.scrollHeight,\n  document.body.offsetHeight, document.documentElement.offsetHeight,\n  document.body.clientHeight, document.documentElement.clientHeight\n);\n\nalert('Full document height, with scrolled out part: ' + scrollHeight);\n```\n\nWhy so? Better don't ask. These inconsistencies come from ancient times, not a \"smart\" logic.\n\n## Get the current scroll [#page-scroll]\n\nDOM elements have their current scroll state in their `scrollLeft/scrollTop` properties.\n\nFor document scroll, `document.documentElement.scrollLeft/scrollTop` works in most browsers, except older WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement`.\n\nLuckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties, `window.pageXOffset/pageYOffset`:\n\n```js run\nalert('Current scroll from the top: ' + window.pageYOffset);\nalert('Current scroll from the left: ' + window.pageXOffset);\n```\n\nThese properties are read-only.\n\n```smart header=\"Also available as `window` properties `scrollX` and `scrollY`\"\nFor historical reasons, both properties exist, but they are the same:\n- `window.pageXOffset` is an alias of `window.scrollX`.\n- `window.pageYOffset` is an alias of `window.scrollY`.\n```\n\n## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll]\n\n```warn\nTo scroll the page with JavaScript, its DOM must be fully built.\n\nFor instance, if we try to scroll the page with a script in `<head>`, it won't work.\n```\n\nRegular elements can be scrolled by changing `scrollTop/scrollLeft`.\n\nWe can do the same for the page using `document.documentElement.scrollTop/scrollLeft` (except Safari, where `document.body.scrollTop/Left` should be used instead).\n\nAlternatively, there's a simpler, universal solution: special methods [window.scrollBy(x,y)](mdn:api/Window/scrollBy) and [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo).\n\n- The method `scrollBy(x,y)` scrolls the page *relative to its current position*. For instance, `scrollBy(0,10)` scrolls the page `10px` down.\n\n    ```online\n    The button below demonstrates this:\n\n    <button onclick=\"window.scrollBy(0,10)\">window.scrollBy(0,10)</button>\n    ```\n- The method `scrollTo(pageX,pageY)` scrolls the page *to absolute coordinates*, so that the top-left corner of the visible part has coordinates `(pageX, pageY)` relative to the document's top-left corner. It's like setting `scrollLeft/scrollTop`.\n\n    To scroll to the very beginning, we can use `scrollTo(0,0)`.\n\n    ```online\n    <button onclick=\"window.scrollTo(0,0)\">window.scrollTo(0,0)</button>\n    ```\n\nThese methods work for all browsers the same way.\n\n## scrollIntoView\n\nFor completeness, let's cover one more method: [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView).\n\nThe call to `elem.scrollIntoView(top)` scrolls the page to make `elem` visible. It has one argument:\n\n- If `top=true` (that's the default), then the page will be scrolled to make `elem` appear on the top of the window. The upper edge of the element will be aligned with the window top.\n- If `top=false`, then the page scrolls to make `elem` appear at the bottom. The bottom edge of the element will be aligned with the window bottom.\n\n```online\nThe button below scrolls the page to position itself at the window top:\n\n<button onclick=\"this.scrollIntoView()\">this.scrollIntoView()</button>\n\nAnd this button scrolls the page to position itself at the bottom:\n\n<button onclick=\"this.scrollIntoView(false)\">this.scrollIntoView(false)</button>\n```\n\n## Forbid the scrolling\n\nSometimes we need to make the document \"unscrollable\". For instance, when we need to cover the page with a large message requiring immediate attention, and we want the visitor to interact with that message, not with the document.\n\nTo make the document unscrollable, it's enough to set `document.body.style.overflow = \"hidden\"`. The page will \"freeze\" at its current scroll position.\n\n```online\nTry it:\n\n<button onclick=\"document.body.style.overflow = 'hidden'\">document.body.style.overflow = 'hidden'</button>\n\n<button onclick=\"document.body.style.overflow = ''\">document.body.style.overflow = ''</button>\n\nThe first button freezes the scroll, while the second one releases it.\n```\n\nWe can use the same technique to freeze the scroll for other elements, not just for `document.body`.\n\nThe drawback of the method is that the scrollbar disappears. If it occupied some space, then that space is now free and the content \"jumps\" to fill it.\n\nThat looks a bit odd, but can be worked around if we compare `clientWidth` before and after the freeze. If it increased (the scrollbar disappeared), then add `padding` to `document.body` in place of the scrollbar to keep the content width the same.\n\n## Summary\n\nGeometry:\n\n- Width/height of the visible part of the document (content area width/height): `document.documentElement.clientWidth/clientHeight`\n- Width/height of the whole document, with the scrolled out part:\n\n    ```js\n    let scrollHeight = Math.max(\n      document.body.scrollHeight, document.documentElement.scrollHeight,\n      document.body.offsetHeight, document.documentElement.offsetHeight,\n      document.body.clientHeight, document.documentElement.clientHeight\n    );\n    ```\n\nScrolling:\n\n- Read the current scroll: `window.pageYOffset/pageXOffset`.\n- Change the current scroll:\n\n    - `window.scrollTo(pageX,pageY)` -- absolute coordinates,\n    - `window.scrollBy(x,y)` -- scroll relative the current place,\n    - `elem.scrollIntoView(top)` -- scroll to make `elem` visible (align with the top/bottom of the window).\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.md",
    "content": "# Outer corners\n\nOuter corners are basically what we get from [elem.getBoundingClientRect()](https://developer.mozilla.org/en-US/docs/DOM/element.getBoundingClientRect).\n\nCoordinates of the upper-left corner `answer1` and the bottom-right corner `answer2`:\n\n```js\nlet coords = elem.getBoundingClientRect();\n\nlet answer1 = [coords.left, coords.top];\nlet answer2 = [coords.right, coords.bottom];\n```\n\n# Left-upper inner corner\n\nThat differs from the outer corner by the border width. A reliable way to get the distance is `clientLeft/clientTop`:\n\n```js\nlet answer3 = [coords.left + field.clientLeft, coords.top + field.clientTop];\n```\n\n# Right-bottom inner corner\n\nIn our case we need to substract the border size from the outer coordinates.\n\nWe could use CSS way:\n\n```js\nlet answer4 = [\n  coords.right - parseInt(getComputedStyle(field).borderRightWidth),\n  coords.bottom - parseInt(getComputedStyle(field).borderBottomWidth)\n];\n```\n\nAn alternative way would be to add `clientWidth/clientHeight` to coordinates of the left-upper corner. That's probably even better:\n\n```js\nlet answer4 = [\n  coords.left + elem.clientLeft + elem.clientWidth,\n  coords.top + elem.clientTop + elem.clientHeight\n];\n```\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.css",
    "content": "body {\n  padding: 20px 0 0 20px;\n  cursor: pointer;\n}\n\n#field {\n  overflow: hidden;\n  width: 200px;\n  height: 150px;\n  border-top: 10px solid black;\n  border-right: 10px solid gray;\n  border-bottom: 10px solid gray;\n  border-left: 10px solid black;\n  background-color: #00FF00;\n  font: 10px/1.2 monospace;\n}\n\n.triangle-right {\n  position: relative;\n  width: 0;\n  height: 0;\n  border-top: 6px solid transparent;\n  border-bottom: 6px solid transparent;\n  border-left: 20px solid red;\n  text-indent: -20px;\n  font: 12px/1 monospace;\n}"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n  <script>\n    document.onclick = function(e) { // shows click coordinates\n      coords.innerHTML = e.clientX + ':' + e.clientY;\n    };\n  </script>\n</head>\n\n<body>\n\n  Click anywhere to get window coordinates.\n  <br> That's for testing, to check the result you get by JavaScript.\n  <br>\n  <div id=\"coords\">(click coordinates show up here)</div>\n\n\n  <div id=\"field\">\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <div class=\"triangle-right\" style=\"left:-20px;top:-176px\">1</div>\n  <div class=\"triangle-right\" style=\"left:-10px;top:-178px\">3</div>\n  <div class=\"triangle-right\" style=\"left:190px;top:-40px\">4</div>\n  <div class=\"triangle-right\" style=\"left:200px;top:-42px\">2</div>\n\n  <script>\n    let fieldCoords = field.getBoundingClientRect();\n\n    let answer = [\n      [ // 1\n        fieldCoords.left,\n        fieldCoords.top\n      ],\n      [ // 2\n        fieldCoords.right,\n        fieldCoords.bottom\n      ],\n      [ // 3\n        fieldCoords.left + field.clientLeft,\n        fieldCoords.top + field.clientTop\n      ],\n      [ // 4\n        fieldCoords.left + field.clientLeft + field.clientWidth,\n        fieldCoords.top + field.clientTop + field.clientHeight\n      ]\n    ];\n\n    alert(answer.join('  '));\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.css",
    "content": "body {\n  padding: 20px 0 0 20px;\n  cursor: pointer;\n}\n\n#field {\n  overflow: hidden;\n  width: 200px;\n  height: 150px;\n  border-top: 10px solid black;\n  border-right: 10px solid gray;\n  border-bottom: 10px solid gray;\n  border-left: 10px solid black;\n  background-color: #00FF00;\n  font: 10px/1.2 monospace;\n}\n\n.triangle-right {\n  position: relative;\n  width: 0;\n  height: 0;\n  border-top: 6px solid transparent;\n  border-bottom: 6px solid transparent;\n  border-left: 20px solid red;\n  text-indent: -20px;\n  font: 12px/1 monospace;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n  <script>\n    document.onclick = function(e) { // shows click coordinates\n      coords.innerHTML = e.clientX + ':' + e.clientY;\n    };\n  </script>\n</head>\n\n<body>\n\n  Click anywhere to get window coordinates.\n  <br> That's for testing, to check the result you get by JavaScript.\n  <br>\n  <div id=\"coords\">(click coordinates show up here)</div>\n\n\n  <div id=\"field\">\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n  <div class=\"triangle-right\" style=\"left:-20px;top:-176px\">1</div>\n  <div class=\"triangle-right\" style=\"left:-10px;top:-178px\">3</div>\n  <div class=\"triangle-right\" style=\"left:190px;top:-40px\">4</div>\n  <div class=\"triangle-right\" style=\"left:200px;top:-42px\">2</div>\n\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/1-find-point-coordinates/task.md",
    "content": "importance: 5\n\n---\n\n# Find window coordinates of the field\n\nIn the iframe below you can see a document with the green \"field\".\n\nUse JavaScript to find window coordinates of corners pointed by with arrows.\n\nThere's a small feature implemented in the document for convenience. A click at any place shows coordinates there.\n\n[iframe border=1 height=360 src=\"source\" link edit]\n\nYour code should use DOM to get window coordinates of:\n\n1. Upper-left, outer corner (that's simple).\n2. Bottom-right, outer corner (simple too).\n3. Upper-left, inner corner (a bit harder).\n4. Bottom-right, inner corner (there are several ways, choose one).\n\nThe coordinates that you calculate should be the same as those returned by the mouse click.\n\nP.S. The code should also work if the element has another size or border, not bound to any fixed values.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.md",
    "content": "In this task we only need to accurately calculate the coordinates. See the code for details.\n\nPlease note: the elements must be in the document to read `offsetHeight` and other properties.\nA hidden (`display:none`) or out of the document element has no size.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.view/index.css",
    "content": ".note {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n    /**\n     * Positions elem relative to anchor as said in position.\n     *\n     * @param {Node} anchor     Anchor element for positioning\n     * @param {string} position One of: top/right/bottom\n     * @param {Node} elem       Element to position\n     *\n     * Both elements: elem and anchor must be in the document\n     */\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = anchor.getBoundingClientRect();\n\n      switch (position) {\n        case \"top\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n    /**\n     * Shows a note with the given html at the given position\n     * relative to the anchor element.\n     */\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/source.view/index.css",
    "content": ".note {\n  position: fixed;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n    /**\n     * Positions elem relative to anchor as said in position.\n     *\n     * @param {Node} anchor     Anchor element for positioning\n     * @param {string} position One of: top/right/bottom\n     * @param {Node} elem       Element to position\n     *\n     * Both elements: elem and anchor must be in the document\n     */\n    function positionAt(anchor, position, elem) {\n      // ... your code ...\n    }\n\n    /**\n     * Shows a note with the given html at the given position\n     * relative to the anchor element.\n     */\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/2-position-at/task.md",
    "content": "importance: 5\n\n---\n\n# Show a note near the element\n\nCreate a function `positionAt(anchor, position, elem)` that positions `elem`, depending on `position` near `anchor` element.\n\nThe `position` must be a string with any one of 3 values:\n- `\"top\"` - position `elem` right above `anchor`\n- `\"right\"` - position `elem` immediately at the right of `anchor`\n- `\"bottom\"` - position `elem` right below `anchor`\n\nIt's used inside function `showNote(anchor, position, html)`, provided in the task source code, that creates a \"note\" element with given `html` and shows it at the given `position` near the `anchor`.\n\nHere's the demo of notes:\n\n[iframe src=\"solution\" height=\"350\" border=\"1\" link]\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.md",
    "content": "The solution is actually pretty simple:\n\n- Use `position:absolute` in CSS instead of `position:fixed` for `.note`.\n- Use the function [getCoords()](info:coordinates#getCoords) from the chapter <info:coordinates> to get document-relative coordinates.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.css",
    "content": ".note {\n  position: absolute;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body style=\"height: 2000px\">\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n\n  <script>\n\n    function getCoords(elem) {\n      let box = elem.getBoundingClientRect();\n\n      return {\n        top: box.top + window.pageYOffset,\n        left: box.left + window.pageXOffset\n      };\n    }\n\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = getCoords(anchor);\n\n      switch (position) {\n        case \"top\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    // test it\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top\", \"note above\");\n    showNote(blockquote, \"right\", \"note at the right\");\n    showNote(blockquote, \"bottom\", \"note below\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/3-position-at-absolute/task.md",
    "content": "importance: 5\n\n---\n\n# Show a note near the element (absolute)\n\nModify the solution of the [previous task](info:task/position-at) so that the note uses `position:absolute` instead of `position:fixed`.\n\nThat will prevent its \"runaway\" from the element when the page scrolls.\n\nTake the solution of that task as a starting point. To test the scroll, add the style `<body style=\"height: 2000px\">`.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.css",
    "content": ".note {\n  position: absolute;\n  z-index: 1000;\n  padding: 5px;\n  border: 1px solid black;\n  background: white;\n  text-align: center;\n  font: italic 14px serif;\n  opacity: .8;\n}\n\nblockquote {\n  background: #f9f9f9;\n  border-left: 10px solid #ccc;\n  margin: 0 0 0 100px;\n  padding: .5em 10px;\n  quotes: \"\\201C\"\"\\201D\"\"\\2018\"\"\\2019\";\n  display: inline-block;\n  white-space: pre;\n}\n\nblockquote:before {\n  color: #ccc;\n  content: open-quote;\n  font-size: 4em;\n  line-height: .1em;\n  margin-right: .25em;\n  vertical-align: -.4em;\n}\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"index.css\">\n</head>\n\n<body style=\"height: 2000px\">\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <blockquote>\n    Teacher: Why are you late?\n    Student: There was a man who lost a hundred dollar bill.\n    Teacher: That's nice. Were you helping him look for it?\n    Student: No. I was standing on it.\n  </blockquote>\n\n  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Reprehenderit sint atque dolorum fuga ad incidunt voluptatum error fugiat animi amet! Odio temporibus nulla id unde quaerat dignissimos enim nisi rem provident molestias sit tempore omnis recusandae\n    esse sequi officia sapiente.</p>\n\n  <script>\n    function getCoords(elem) {\n      let box = elem.getBoundingClientRect();\n\n      return {\n        top: box.top + window.pageYOffset,\n        left: box.left + window.pageXOffset\n      };\n    }\n\n    function showNote(anchor, position, html) {\n\n      let note = document.createElement('div');\n      note.className = \"note\";\n      note.innerHTML = html;\n      document.body.append(note);\n\n      positionAt(anchor, position, note);\n    }\n\n    function positionAt(anchor, position, elem) {\n\n      let anchorCoords = getCoords(anchor);\n\n      switch (position) {\n        case \"top-out\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top - elem.offsetHeight + \"px\";\n          break;\n\n        case \"right-out\":\n          elem.style.left = anchorCoords.left + anchor.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom-out\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight + \"px\";\n          break;\n\n        case \"top-in\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"right-in\":\n          elem.style.width = '150px';\n          elem.style.left = anchorCoords.left + anchor.offsetWidth - elem.offsetWidth + \"px\";\n          elem.style.top = anchorCoords.top + \"px\";\n          break;\n\n        case \"bottom-in\":\n          elem.style.left = anchorCoords.left + \"px\";\n          elem.style.top = anchorCoords.top + anchor.offsetHeight - elem.offsetHeight + \"px\";\n          break;\n      }\n\n    }\n\n\n    let blockquote = document.querySelector('blockquote');\n\n    showNote(blockquote, \"top-in\", \"note top-in\");\n    showNote(blockquote, \"top-out\", \"note top-out\");\n    showNote(blockquote, \"right-out\", \"note right-out\");\n    showNote(blockquote, \"bottom-in\", \"note bottom-in\");\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/4-position-inside-absolute/task.md",
    "content": "importance: 5\n\n---\n\n# Position the note inside (absolute)\n\nExtend the previous task <info:task/position-at-absolute>: teach the function  `positionAt(anchor, position, elem)` to insert `elem` inside the `anchor`.\n\nNew values for `position`:\n\n- `top-out`, `right-out`, `bottom-out` -- work the same as before, they insert the `elem` over/right/under `anchor`.\n- `top-in`, `right-in`, `bottom-in` -- insert `elem` inside the `anchor`: stick it to the upper/right/bottom edge.\n\nFor instance:\n\n```js\n// shows the note above blockquote\npositionAt(blockquote, \"top-out\", note);\n\n// shows the note inside blockquote, at the top\npositionAt(blockquote, \"top-in\", note);\n```\n\nThe result:\n\n[iframe src=\"solution\" height=\"310\" border=\"1\" link]\n\nAs the source code, take the solution of the task <info:task/position-at-absolute>.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/article.md",
    "content": "# Coordinates\n\nTo move elements around we should be familiar with coordinates.\n\nMost JavaScript methods deal with one of two coordinate systems:\n\n1. **Relative to the window** - similar to `position:fixed`, calculated from the window top/left edge.\n    - we'll denote these coordinates as `clientX/clientY`, the reasoning for such name will become clear later, when we study event properties.\n2. **Relative to the document** - similar to `position:absolute` in the document root, calculated from the document top/left edge.\n    - we'll denote them `pageX/pageY`.\n\nWhen the page is scrolled to the very beginning, so that the top/left corner of the window is exactly the document top/left corner, these coordinates equal each other. But after the document shifts, window-relative coordinates of elements change, as elements move across the window, while document-relative coordinates remain the same.\n\nOn this picture we take a point in the document and demonstrate its coordinates before the scroll (left) and after it (right):\n\n![](document-and-window-coordinates-scrolled.svg)\n\nWhen the document scrolled:\n- `pageY` - document-relative coordinate stayed the same, it's counted from the document top (now scrolled out).\n- `clientY` - window-relative coordinate did change (the arrow became shorter), as the same point became closer to window top.\n\n## Element coordinates: getBoundingClientRect\n\nThe method `elem.getBoundingClientRect()` returns window coordinates for a minimal rectangle that encloses `elem` as an object of built-in [DOMRect](https://www.w3.org/TR/geometry-1/#domrect) class.\n\nMain `DOMRect` properties:\n\n- `x/y` -- X/Y-coordinates of the rectangle origin relative to window,\n- `width/height` -- width/height of the rectangle (can be negative).\n\nAdditionally, there are derived properties:\n\n- `top/bottom` -- Y-coordinate for the top/bottom rectangle edge,\n- `left/right` -- X-coordinate for the left/right rectangle edge.\n\n```online\nFor instance click this button to see its window coordinates:\n\n<p><input id=\"brTest\" type=\"button\" value=\"Get coordinates using button.getBoundingClientRect() for this button\" onclick='showRect(this)'/></p>\n\n<script>\nfunction showRect(elem) {\n  let r = elem.getBoundingClientRect();\n  alert(`x:${r.x}\ny:${r.y}\nwidth:${r.width}\nheight:${r.height}\ntop:${r.top}\nbottom:${r.bottom}\nleft:${r.left}\nright:${r.right}\n`);\n}\n</script>\n\nIf you scroll the page and repeat, you'll notice that as window-relative button position changes, its window coordinates (`y/top/bottom` if you scroll vertically) change as well.\n```\n\nHere's the picture of `elem.getBoundingClientRect()` output:\n\n![](coordinates.svg)\n\nAs you can see, `x/y` and `width/height` fully describe the rectangle. Derived properties can be easily calculated from them:\n\n- `left = x`\n- `top = y`\n- `right = x + width`\n- `bottom = y + height`\n\nPlease note:\n\n- Coordinates may be decimal fractions, such as `10.5`. That's normal, internally browser uses fractions in calculations. We don't have to round them when setting to `style.left/top`.\n- Coordinates may be negative. For instance, if the page is scrolled so that `elem` is now above the window, then `elem.getBoundingClientRect().top` is negative.\n\n```smart header=\"Why derived properties are needed? Why does `top/left` exist if there's `x/y`?\"\nMathematically, a rectangle is uniquely defined with its starting point `(x,y)` and the direction vector `(width,height)`. So the additional derived properties are for convenience.\n\nTechnically it's possible for `width/height` to be negative, that allows for \"directed\" rectangle, e.g. to represent mouse selection with properly marked start and end.\n\nNegative `width/height` values mean that the rectangle starts at its bottom-right corner and then \"grows\" left-upwards.\n\nHere's a rectangle with negative `width` and `height` (e.g. `width=-200`, `height=-100`):\n\n![](coordinates-negative.svg)\n\nAs you can see, `left/top` do not equal `x/y` in such case.\n\nIn practice though, `elem.getBoundingClientRect()` always returns positive width/height, here we mention negative `width/height` only for you to understand why these seemingly duplicate properties are not actually duplicates.\n```\n\n```warn header=\"Internet Explorer: no support for `x/y`\"\nInternet Explorer doesn't support `x/y` properties for historical reasons.\n\nSo we can either make a polyfill (add getters in `DomRect.prototype`) or just use `top/left`, as they are always the same as `x/y` for positive `width/height`, in particular in the result of `elem.getBoundingClientRect()`.\n```\n\n```warn header=\"Coordinates right/bottom are different from CSS position properties\"\nThere are obvious similarities between window-relative coordinates and CSS `position:fixed`.\n\nBut in CSS positioning, `right` property means the distance from the right edge, and `bottom` property means the distance from the bottom edge.\n\nIf we just look at the picture above, we can see that in JavaScript it is not so. All window coordinates are counted from the top-left corner, including these ones.\n```\n\n## elementFromPoint(x, y) [#elementFromPoint]\n\nThe call to `document.elementFromPoint(x, y)` returns the most nested element at window coordinates `(x, y)`.\n\nThe syntax is:\n\n```js\nlet elem = document.elementFromPoint(x, y);\n```\n\nFor instance, the code below highlights and outputs the tag of the element that is now in the middle of the window:\n\n```js run\nlet centerX = document.documentElement.clientWidth / 2;\nlet centerY = document.documentElement.clientHeight / 2;\n\nlet elem = document.elementFromPoint(centerX, centerY);\n\nelem.style.background = \"red\";\nalert(elem.tagName);\n```\n\nAs it uses window coordinates, the element may be different depending on the current scroll position.\n\n````warn header=\"For out-of-window coordinates the `elementFromPoint` returns `null`\"\nThe method `document.elementFromPoint(x,y)` only works if `(x,y)` are inside the visible area.\n\nIf any of the coordinates is negative or exceeds the window width/height, then it returns `null`.\n\nHere's a typical error that may occur if we don't check for it:\n\n```js\nlet elem = document.elementFromPoint(x, y);\n// if the coordinates happen to be out of the window, then elem = null\n*!*\nelem.style.background = ''; // Error!\n*/!*\n```\n````\n\n## Using for \"fixed\" positioning\n\nMost of time we need coordinates in order to position something.\n\nTo show something near an element, we can use `getBoundingClientRect` to get its coordinates, and then CSS `position` together with `left/top` (or `right/bottom`).\n\nFor instance, the function `createMessageUnder(elem, html)` below shows the message under `elem`:\n\n```js\nlet elem = document.getElementById(\"coords-show-mark\");\n\nfunction createMessageUnder(elem, html) {\n  // create message element\n  let message = document.createElement('div');\n  // better to use a css class for the style here\n  message.style.cssText = \"position:fixed; color: red\";\n\n*!*\n  // assign coordinates, don't forget \"px\"!\n  let coords = elem.getBoundingClientRect();\n\n  message.style.left = coords.left + \"px\";\n  message.style.top = coords.bottom + \"px\";\n*/!*\n\n  message.innerHTML = html;\n\n  return message;\n}\n\n// Usage:\n// add it for 5 seconds in the document\nlet message = createMessageUnder(elem, 'Hello, world!');\ndocument.body.append(message);\nsetTimeout(() => message.remove(), 5000);\n```\n\n```online\nClick the button to run it:\n\n<button id=\"coords-show-mark\">Button with id=\"coords-show-mark\", the message will appear under it</button>\n```\n\nThe code can be modified to show the message at the left, right, below, apply CSS animations to \"fade it in\" and so on. That's easy, as we have all the coordinates and sizes of the element.\n\nBut note the important detail: when the page is scrolled, the message flows away from the button.\n\nThe reason is obvious: the message element relies on `position:fixed`, so it remains at the same place of the window while the page scrolls away.\n\nTo change that, we need to use document-based coordinates and `position:absolute`.\n\n## Document coordinates [#getCoords]\n\nDocument-relative coordinates start from the upper-left corner of the document, not the window.\n\nIn CSS, window coordinates correspond to `position:fixed`, while document coordinates are similar to `position:absolute` on top.\n\nWe can use `position:absolute` and `top/left` to put something at a certain place of the document, so that it remains there during a page scroll. But we need the right coordinates first.\n\nThere's no standard method to get the document coordinates of an element. But it's easy to write it.\n\nThe two coordinate systems are connected by the formula:\n- `pageY` = `clientY` + height of the scrolled-out vertical part of the document.\n- `pageX` = `clientX` + width of the scrolled-out horizontal part of the document.\n\nThe function `getCoords(elem)` will take window coordinates from `elem.getBoundingClientRect()` and add the current scroll to them:\n\n```js\n// get document coordinates of the element\nfunction getCoords(elem) {\n  let box = elem.getBoundingClientRect();\n\n  return {\n    top: box.top + window.pageYOffset,\n    right: box.right + window.pageXOffset,\n    bottom: box.bottom + window.pageYOffset,\n    left: box.left + window.pageXOffset\n  };\n}\n```\n\nIf in the example above we used it with `position:absolute`, then the message would stay near the element on scroll.\n\nThe modified `createMessageUnder` function:\n\n```js\nfunction createMessageUnder(elem, html) {\n  let message = document.createElement('div');\n  message.style.cssText = \"*!*position:absolute*/!*; color: red\";\n\n  let coords = *!*getCoords(elem);*/!*\n\n  message.style.left = coords.left + \"px\";\n  message.style.top = coords.bottom + \"px\";\n\n  message.innerHTML = html;\n\n  return message;\n}\n```\n\n## Summary\n\nAny point on the page has coordinates:\n\n1. Relative to the window -- `elem.getBoundingClientRect()`.\n2. Relative to the document -- `elem.getBoundingClientRect()` plus the current page scroll.\n\nWindow coordinates are great to use with `position:fixed`, and document coordinates do well with `position:absolute`.\n\nBoth coordinate systems have their pros and cons; there are times we need one or the other one, just like CSS `position` `absolute` and `fixed`.\n"
  },
  {
    "path": "2-ui/1-document/11-coordinates/head.html",
    "content": "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n\n  let elem = document.getElementById('coords-show-mark');\n\n  // no elem in ebook (pdf/epub) mode\n  if (elem) {\n    elem.onclick = function() {\n\n      function createMessageUnder(elem, text) {\n        let coords = elem.getBoundingClientRect();\n        let message = document.createElement('div');\n        message.style.cssText = \"position:fixed; color: red\";\n\n        message.style.left = coords.left + \"px\";\n        message.style.top = coords.bottom + \"px\";\n\n        message.innerHTML = text;\n\n        return message;\n      }\n\n      let message = createMessageUnder(elem, 'Hello, world!');\n      document.body.append(message);\n      setTimeout(() => message.remove(), 5000);\n    }\n  }\n\n});\n\n</script>\n"
  },
  {
    "path": "2-ui/1-document/index.md",
    "content": "# Document\n\nHere we'll learn to manipulate a web-page using JavaScript.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <input type=\"button\" id=\"hider\" value=\"Klik disini untuk menyembunyikan pesan\" />\n\n  <div id=\"text\">Teks</div>\n\n  <script>\n    // Disini tidak terlalu penting bagaimana cara menyembunyikan teks,\n    // bisa juga digunakan style.display\n    document.getElementById('hider').onclick = function() {\n      document.getElementById('text').hidden = true;\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <input type=\"button\" id=\"hider\" value=\"Klik disini untuk menyembunyikan pesan\" />\n\n  <div id=\"text\">Teks</div>\n\n  <script>\n    /* kode kamu */\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/01-hide-other/task.md",
    "content": "importance: 5\n\n---\n\n# Klik untuk menyembunyikan\n\nTambah Javascript ke `button` untuk membuat `<div id=\"text\">` hilang pada saat di klik.\n\ndemo:\n\n[iframe border=1 src=\"solution\" height=80]\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/solution.md",
    "content": "Bisa gunakan `this` pada penangan (_handler_) untuk mereferensi \"elemen itu sendiri\":\n\n```html run height=50\n<input type=\"button\" onclick=\"this.hidden=true\" value=\"Klik untuk menyembunyikan\">\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/02-hide-self-onclick/task.md",
    "content": "importance: 5\n\n---\n\n# Sembunyikan diri\n\nBuat sebuah tombol yang menyembunyikan dirinya sendiri pada saat di klik.\n\n```online\nSeperti ini:\n<input type=\"button\" onclick=\"this.hidden=true\" value=\"Klik untuk sembunyi\">\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/solution.md",
    "content": "Jawabannya: `1` dan `2`.\n\npenangan pertama dijalankan, karena tidak di hapuskan oleh `removeEventListener`. Untuk menghapuskan penangan kita harus meneruskan secara tepat fungsi yang telah di atur. Dan pada kode sebuah fungsi baru di teruskan, terlihat sama, tapi berbeda fungsi.\n\nUntuk menghapuskan objek fungsi, kita harus menyimpan refensi ke fungsi tersebut, seperti ini:\n\n```js\nfunction handler() {\n  alert(1);\n}\n\nbutton.addEventListener(\"click\", handler);\nbutton.removeEventListener(\"click\", handler);\n```\n\npenangan `button.onclick` bekerja secara sendiri dan sebagai tambahan untuk `addEvenetListener`.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/03-which-handlers-run/task.md",
    "content": "importance: 5\n\n---\n\n# Penangan mana yang dijalankan?\n\nAda sebuah tombol pada variable. Tidak ada pengedali di tombol tersebut.\n\nManakah penangan yang dijalankan pada saat klik pada kode berikut ini? Manakah `alert` yang akan ditunjukan?\n\n```js no-beautify\nbutton.addEventListener(\"click\", () => alert(\"1\"));\n\nbutton.removeEventListener(\"click\", () => alert(\"1\"));\n\nbutton.onclick = () => alert(2);\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.md",
    "content": "\nPertama kita perlu memilih metode untuk memposisikan bola.\n\nKita tidak dapat menggunakan `position:fixed` untuk itu, karena pada saat halaman digulir bola akan berpindah dari lapangan.\n\nJadi kita sebaiknya menggunakan `position:absolute` dan, untuk membuat posisinya benar-benar tepat, buat lapangan(_`field`_) itu sendiri diposisikan.\n\nKemudian bola akan diposisikan relatif terhadap lapangan(_`field`_):\n\n```css\n#field {\n  width: 200px;\n  height: 150px;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  left: 0; /* relatif ke posisi terdekat blok atas (lapangan) */\n  top: 0;\n  transition: 1s all; /* Animasi CSS untuk left/top untuk membuat bolanya terbang */\n}\n```\n\nSelanjutnya kita harus mengatur dengan benar `ball.style.left/top`. Mereka memiliki koordinat relatif ke lapangan(_`field`_) sekarang.\n\nBerikut ini gambarnya:\n\n![](move-ball-coords.svg)\n\nKita memiliki `event.clientX/clientY` -- koordinat relatif jendela (_window_) dari klik.\n\nUntuk mendapatkan koordinat kiri relatif terhadap lapangan(_`field`_), kita kurangkan dengan nilai dari ujung dan pembatas kiri lapangan:\n\n```js\nlet left = event.clientX - fieldCoords.left - field.clientLeft;\n```\n\nBiasanya, `ball.style.left` artinya \"ujung kiri dari elemen\" (bola). Jadi kita mengatur nilai `left`, kemudian ujung bola, bukan tengah, akan berada tepat dibawah kursor mouse.\n\nKita perlu memindahkan bola setengah lebar kiri dan setengah tinggi atas untuk membuatnya di tengah.\n\nJadi nilai terakhir `left` akan menjadi:\n\n```js\nlet left = event.clientX - fieldCoords.left - field.clientLeft - ball.offsetWidth/2;\n```\n\nKoordinat vertikal di ukur dengan menggunakan logika yang sama.\n\nHarap dicatat bahwa lebar/tinggi bola harus di ketahui pada saat kita mengakses `ball.offsetWidth`. Sebaiknya di atur pada HTML atau CSS.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #field {\n      width: 200px;\n      height: 150px;\n      border: 10px solid black;\n      background-color: #00FF00;\n      position: relative;\n      overflow: hidden;\n      cursor: pointer;\n    }\n\n    #ball {\n      position: absolute;\n      left: 0;\n      top: 0;\n      width: 40px;\n      height: 40px;\n      transition: all 1s;\n    }\n  </style>\n</head>\n\n<body style=\"height:2000px\">\n\n  Klik pada lapangan untuk memindahkan bola kesana.\n  <br>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n  <script>\n    field.onclick = function(event) {\n\n      // Koordinat lapangan relatif terhadap jendela (window)\n      let fieldCoords = this.getBoundingClientRect();\n\n      // bola memiliki position:absolute, lapangan: position:relative\n      // jadi koordinat bola relatif terhadap sudut dalam kiri atas lapangan\n      let ballCoords = {\n        top: event.clientY - fieldCoords.top - field.clientTop - ball.clientHeight / 2,\n        left: event.clientX - fieldCoords.left - field.clientLeft - ball.clientWidth / 2\n      };\n\n      // mencegah melewati batas atas lapangan\n      if (ballCoords.top < 0) ballCoords.top = 0;\n\n      // mencegah melewati kiri atas lapangan\n      if (ballCoords.left < 0) ballCoords.left = 0;\n\n\n      // mencegah melewati batas kanan lapangan\n      if (ballCoords.left + ball.clientWidth > field.clientWidth) {\n        ballCoords.left = field.clientWidth - ball.clientWidth;\n      }\n\n      // mencegah melewati batas bawah lapangan\n      if (ballCoords.top + ball.clientHeight > field.clientHeight) {\n        ballCoords.top = field.clientHeight - ball.clientHeight;\n      }\n\n      ball.style.left = ballCoords.left + 'px';\n      ball.style.top = ballCoords.top + 'px';\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #field {\n      width: 200px;\n      height: 150px;\n      border: 10px solid black;\n      background-color: #00FF00;\n      overflow: hidden;\n    }\n  </style>\n</head>\n\n<body style=\"height:2000px\">\n  \n  Klik pada lapangan untuk memindahkan bola kesana.\n  <br> Bola tidak boleh meninggalkan lapangan. \n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\"> . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n    . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n  </div>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/04-move-ball-field/task.md",
    "content": "importance: 5\n\n---\n\n# Pindahkan bola ke seberang lapangan\n\nPindahkan bola ke seberang lapangan pada saat di klik. Seperti ini:\n\n[iframe src=\"solution\" height=\"260\" link]\n\nSyarat-Syarat:\n\n- Pusat bola harus berada tepat dibawah pointer pada saat di klik (jika memungkinkan tanpa melintasi ujung lapangan).\n- Animasi CSS jika memungkinkan.\n- Bola tidak boleh melintasi batas lapangan.\n- Saat halaman di digulir, tidak ada yang rusak.\n\nTambahan:\n\n- Kode harus juga bekerja dengan bola yang berbeda dan berbagai ukuran lapangan, tidak hanya pada ukuran tertentu.\n- Gunakan properti `event.clientX/event.clientY` untuk koordinat klik.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.md",
    "content": "\n# HTML/CSS\nPertama buat HTML/CSS.\n\nSebuah menu adalah komponen tersendiri pada halaman, jadi lebih baik untuk menaruhnya kedalam satu elemen DOM.\n\nSebuah daftar dari item menu bisa dapat diatur kedalam daftar menggunakan `ul/li`.\n\nIni contoh strukturnya:\n\n```html\n<div class=\"menu\">\n  <span class=\"title\">Manis-Manis (Tekan saya)!</span>\n  <ul>\n    <li>Kue</li>\n    <li>Donat</li>\n    <li>Madu</li>\n  </ul>\n</div>\n```\n\nKita dapat menggunakan `<span>` untuk judul, karena `<div>` memiliki `display:block`, dan akan memenuhi 100% horisontal lebar elemen.\n\nSeperti ini:\n\n```html autorun height=50\n<div style=\"border: solid red 1px\" onclick=\"alert(1)\">Manis-Manis (Tekan saya)!</div>\n```\n\nJadi jika kita mengatur `onclick` pada judul, maka itu akan menangkap klik diselah kanan teks.\n\nSedangkan `<span>` memiliki `display:inline`, dan akan memenuhi ruang yang cukup sesuai dengan teks:\n\n```html autorun height=50\n<span style=\"border: solid red 1px\" onclick=\"alert(1)\">Manis-Manis (Tekan saya)!</span>\n```\n\n# Membuka dan menutup menu\n\nMembuka dan menutup menu seharusnya menganti posisi anak panah dan menunjukan atau menyembunyikan daftar menu.\n\nSemua pergantian ini sangat sempurna untuk di tanggani oleh CSS. Pada Javascript kita harus memberi label pada kondisi menu saat ini dengan menambahkan/menghapuskan kelas(_class_) `.open`.\n\nTanpanya, menu akan tetap tertutup:\n```css\n.menu ul {\n  margin: 0;\n  list-style: none;\n  padding-left: 20px;\n  display: none;\n}\n\n.menu .title::before {\n  content: '▶ ';\n  font-size: 80%;\n  color: green;\n}\n```\n\n...Dan dengan `.open` anak panah akan berubah dan daftar akan kelihatan:\n\n```css\n.menu.open .title::before {\n  content: '▼ ';\n}\n\n.menu.open ul {\n  display: block;\n}\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .menu ul {\n      margin: 0;\n      list-style: none;\n      padding-left: 20px;\n      display: none;\n    }\n\n    .menu .title {\n      font-size: 18px;\n      cursor: pointer;\n    }\n\n    .menu .title::before {\n      content: '▶ ';\n      font-size: 80%;\n      color: green;\n    }\n\n    .menu.buka .title::before {\n      content: '▼ ';\n    }\n\n    .menu.buka ul {\n      display: block;\n    }\n  </style>\n</head>\n\n<body>\n\n  <div id=\"sweeties\" class=\"menu\">\n    <span class=\"title\">Manis-Manis (Klik Saya)!</span>\n    <ul>\n      <li>Kue</li>\n      <li>Donat</li>\n      <li>Madu</li>\n    </ul>\n\n  </div>\n\n  <script>\n    let menuElem = document.getElementById('sweeties');\n    let titleElem = menuElem.querySelector('.title');\n\n    titleElem.onclick = function() {\n      menuElem.classList.toggle('buka');\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  ▶ ▼ Manis-Manis (Klik Saya)!\n  <ul>\n    <li>Kue</li>\n    <li>Donat</li>\n    <li>Madu</li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/05-sliding-menu/task.md",
    "content": "importance: 5\n\n---\n\n# Buat sebuah menu geser\n\nBuat sebuah menu yang terbuka/tertutup pada saat ditekan:\n\n[iframe border=1 height=100 src=\"solution\"]\n\nTambahan: HTML/CSS dari dokumen harus dimodifikasi. \n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.md",
    "content": "\nUntuk Menambahkan tombol kita bisa menggunakan `position:absolute` (dan membuat pane `position:relative`) atau `float:right`. `float:right` memiliki keuntung dimana tombol tidak akan perna tumpang tindih dengan teks, tetapi `position:absolute` memberikan lebih banyak kebebasan. Jadi pilihannya pada kamu.\n\nKemudian pada setiap `pane` kodenya akan seperti ini:\n```js\npane.insertAdjacentHTML(\"afterbegin\", '<button class=\"remove-button\">[x]</button>');\n```\nKemudian `<button>` menjadi `pane.firstChild`, jadi kita tambahkan sebuah penangan seperti ini:\n\n```js\npane.firstChild.onclick = () => pane.remove();\n```\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div>\n    <div class=\"pane\">\n      <h3>Kuda</h3>\n      <p>Kuda adalah salah satu dari dua subspesies Equus ferus yang masih ada. Ini adalah mamalia berkuku aneh milik keluarga taksonomi Equidae. Kuda telah berevolusi selama 45 hingga 55 juta tahun terakhir dari makhluk kecil berjari banyak, Eohippus, menjadi hewan besar berjari satu saat ini.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Keledai</h3>\n      <p>Keledai (Equus africanus asinus) adalah anggota keluarga kuda yang dijinakkan, Equidae. Nenek moyang keledai liar adalah keledai liar Afrika, E. africanus. Keledai telah digunakan sebagai hewan pekerja setidaknya selama 5000 tahun.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Kucing</h3>\n      <p>Kucing domestik (bahasa Latin: Felis catus) adalah mamalia karnivora kecil yang biasanya berbulu. Mereka sering disebut kucing rumah ketika dipelihara sebagai hewan peliharaan dalam ruangan atau hanya kucing ketika tidak perlu membedakan mereka dari felids dan kucing lainnya. Kucing sering dihargai oleh manusia karena persahabatan dan kemampuan mereka untuk berburu hama.\n      </p>\n    </div>\n  </div>\n\n\n  <script>\n    let panes = document.querySelectorAll('.pane');\n\n    for(let pane of panes) {\n      pane.insertAdjacentHTML(\"afterbegin\", '<button class=\"remove-button\">[x]</button>');\n      // Tombol menjadi anak pertama dari `pane`\n      pane.firstChild.onclick = () => pane.remove();\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/solution.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  Kode tombol (Kamu bisa mengubahnya menggunakan CSS):\n  <button class=\"remove-button\">[x]</button>\n\n  <div>\n    <div class=\"pane\">\n      <h3>Kuda</h3>\n      <p>Kuda adalah salah satu dari dua subspesies Equus ferus yang masih ada. Ini adalah mamalia berkuku aneh milik keluarga taksonomi Equidae. Kuda telah berevolusi selama 45 hingga 55 juta tahun terakhir dari makhluk kecil berjari banyak, Eohippus, menjadi hewan besar berjari satu saat ini.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Keledai</h3>\n      <p>Keledai (Equus africanus asinus) adalah anggota keluarga kuda yang dijinakkan, Equidae. Nenek moyang keledai liar adalah keledai liar Afrika, E. africanus. Keledai telah digunakan sebagai hewan pekerja setidaknya selama 5000 tahun.</p>\n    </div>\n    <div class=\"pane\">\n      <h3>Kucing</h3>\n      <p>Kucing domestik (bahasa Latin: Felis catus) adalah mamalia karnivora kecil yang biasanya berbulu. Mereka sering disebut kucing rumah ketika dipelihara sebagai hewan peliharaan dalam ruangan atau hanya kucing ketika tidak perlu membedakan mereka dari felids dan kucing lainnya. Kucing sering dihargai oleh manusia karena persahabatan dan kemampuan mereka untuk berburu hama.\n      </p>\n    </div>\n  </div>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/source.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n}\n\n.remove-button {\n  font-size: 110%;\n  color: darkred;\n  right: 10px;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/06-hide-message/task.md",
    "content": "importance: 5\n\n---\n\n# Tambahkan tombol untuk menutup\n\nAda sebuah daftar pesan.\n\nGunakan Javascript untuk menambahkan tombol untuk menutup pada bagian pojok kanan atas setiap pesan.\n\nHasil seharusnya seperti ini:\n\n[iframe src=\"solution\" height=450]\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.md",
    "content": "Pita gambar bisa di diwakili sebagai `ul/li` daftar dari gambar `<img>`.\n\nBiasanya, pita seperti itu sangat luas, tapi kita akan menambahkan ukuran tetap pada `<div>` untuk \"memotong\" pita, jadi hanya sebagian dari pita yang kelihatan:\n\n![](carousel1.svg)\n\nUntuk menampilkan daftar secara horisontal kita perlu menambahkan properti CSS yang benar pada `<li>`, seperti `display: inline-block`.\n\nUntuk `<img>` kita sebaiknya juga mengatur `display`, karena bawaanya merupakan `inline`. Ada \nFor `<img>` we should also adjust `display`, because by default it's `inline`. Ada ruang ekstra yang disediakan di bawah elemen `inline` untuk \"ekor huruf\", jadi kita bisa menggunakan `display:block` untuk menghapusnya.\n\nUntuk membuat pengulirannya, kita bisa menggeser `<ul>`. Ada banyak cara untuk melakukannya, contohnya dengan menganti `margin-left` atau (performa lebih baik) gunakan `transform: translateX()`:\n\n![](carousel2.svg)\n\n`<div>` luar memiliki lebar tetap, jadi gambar \"ekstra\" dipotong.\n\nKeseluruhan carousel adalah \"komponen grafis\" mandiri pada halaman, jadi sebaiknya kita membungkusnya menjadi satu `<div class=\"carousel\">` dan menata elemen-elemen ke dalamnya.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/index.html",
    "content": "<!DOCTYPE html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"carousel\" class=\"carousel\">\n    <button class=\"arrow prev\">⇦</button>\n    <div class=\"gallery\">\n      <ul class=\"images\">\n        <li><img src=\"https://en.js.cx/carousel/1.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/2.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/3.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/4.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/5.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/6.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/7.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/8.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/9.png\"></li>\n        <li><img src=\"https://en.js.cx/carousel/10.png\"></li>\n      </ul>\n    </div>\n    <button class=\"arrow next\">⇨</button>\n  </div>\n\n  <script>\n    /* Beri label pada gambar untuk memudahkan */\n    let i = 1;\n    for(let li of carousel.querySelectorAll('li')) {\n      li.style.position = 'relative';\n      li.insertAdjacentHTML('beforeend', `<span style=\"position:absolute;left:0;top:0\">${i}</span>`);\n      i++;\n    }\n\n    /* Konfigurasi */\n    let width = 130; // Lebar gambar\n    let count = 3; // Nomor gambar\n\n    let list = carousel.querySelector('ul');\n    let listElems = carousel.querySelectorAll('li');\n\n    let position = 0; // posisi gulir pita\n\n    carousel.querySelector('.prev').onclick = function() {\n      // shift left\n      position += width * count;\n      // tidak bisa bergerak ke kiri terlalu banyak, gambar terakhir\n      position = Math.min(position, 0)\n      list.style.marginLeft = position + 'px';\n    };\n\n    carousel.querySelector('.next').onclick = function() {\n      // shift right\n      position -= width * count;\n      // hanya dapat menggeser pita untuk (panjang pita total - jumlah yang terlihat) gambar\n      position = Math.max(position, -width * (listElems.length - count));\n      list.style.marginLeft = position + 'px';\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/solution.view/style.css",
    "content": "body {\n  padding: 10px;\n}\n\n.carousel {\n  position: relative;\n  width: 398px;\n  padding: 10px 40px;\n  border: 1px solid #CCC;\n  border-radius: 15px;\n  background: #eee;\n}\n\n.carousel img {\n  width: 130px;\n  height: 130px;\n  /* make it block to remove space around images */\n  display: block;\n}\n\n.arrow {\n  position: absolute;\n  top: 60px;\n  padding: 0;\n  background: #ddd;\n  border-radius: 15px;\n  border: 1px solid gray;\n  font-size: 24px;\n  line-height: 24px;\n  color: #444;\n  display: block;\n}\n\n.arrow:focus {\n  outline: none;\n}\n\n.arrow:hover {\n  background: #ccc;\n  cursor: pointer;\n}\n\n.prev {\n  left: 7px;\n}\n\n.next {\n  right: 7px;\n}\n\n.gallery {\n  width: 390px;\n  overflow: hidden;\n}\n\n.gallery ul {\n  height: 130px;\n  width: 9999px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  transition: margin-left 250ms;\n  /* remove white-space between inline-block'ed li */\n  /* http://davidwalsh.name/remove-whitespace-inline-block */\n  font-size: 0;\n}\n\n.gallery li {\n  display: inline-block;\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/index.html",
    "content": "<!DOCTYPE html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <!-- buat markup dan style -->\n\n  <button class=\"arrow\">⇦</button>\n  <button class=\"arrow\">⇨</button>\n\n\n  <ul>\n    <li><img src=\"https://en.js.cx/carousel/1.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/2.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/3.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/4.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/5.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/6.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/7.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/8.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/9.png\"></li>\n    <li><img src=\"https://en.js.cx/carousel/10.png\"></li>\n  </ul>\n\n\n  <script>\n    // Beri label pada gambar untuk memudahkan\n    // kode ini bisa di hapuskan\n    let i = 1;\n    for(let li of carousel.querySelectorAll('li')) {\n      li.style.position = 'relative';\n      li.insertAdjacentHTML('beforeend', `<span style=\"position:absolute;left:0;top:0\">${i}</span>`);\n      i++;\n    }\n\n    // ...kodemu untuk membuat sebuah korsel(carousel)\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/source.view/style.css",
    "content": ".arrow {\n  padding: 0;\n  background: #ddd;\n  border-radius: 15px;\n  border: 1px solid gray;\n  font-size: 24px;\n  line-height: 24px;\n  color: #444;\n  display: block;\n}\n\n.arrow:focus {\n  outline: none;\n}\n\n.arrow:hover {\n  background: #ccc;\n  cursor: pointer;\n}\n\nul {\n  height: 130px;\n  width: 9999px;\n  margin: 0;\n  padding: 0;\n  list-style: none;\n  font-size: 0; \n}\n\nul img {\n  width: 130px;\n  height: 130px;\n  display: block; /* removes extra space near images */\n}\n\nul li {\n  display: inline-block; /* removes extra space between list items\n}\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/07-carousel/task.md",
    "content": "importance: 4\n\n---\n\n# Carousel\n\nBuat sebuah Carousel -- pita gambar yang dapat digulir dengan mengklik panah.\n\n[iframe height=200 src=\"solution\"]\n\nNanti kita akan menambahkah lebih banyak fitur: pengguliran tanpa batas, pemuatan dinamis dan seterusnya.\n\nTambahan: untuk tugas ini, struktur HTML/CSS merupakan 90% dari solusi.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/article.md",
    "content": "# Pengenalan ke peristiwa peramban (_browser events_)\n\n*Sebuah Peristiwa* adalah penanda bahwa sesuatu telah terjadi. Semua _DOM nodes_ menghasilkan sebuah penanda (tapi peristiwa tidak hanya terbatas pada DOM).\n\nBerikut ini daftar peristiwa DOM yang paling berguna:\n\n**Peristiwa mouse (_Mouse events_):**\n- `click` -- pada saat mouse mengklik sebuah elemen (perangkat layar sentuh menghasilkan peristiwa ini pada saat ditekan)\n- `contextmenu` -- pada saat mouse mengklik kanan sebuah elemen.\n- `mouseover` / `mouseout` -- pada saat kursor mouse menghampiri / meninggalkan sebuah elemen.\n- `mousedown` / `mouseup` -- pada saat tombol mouse button ditekan / dilepaskan diatas sebuah elemen.\n- `mousemove` -- pada saat mouse bergerak.\n\n**Peristiwa papan ketik (_Keyboard events_):**\n- `keydown` dan `keyup` -- pada saat tombol papan ketik ditekan dan dilepaskan.\n\n**Peristiwa Elemen form (_Form element events_):**\n- `submit` -- pada saat pengunjung memasukan sebuah `<form>`.\n- `focus` --  pada saat pengunjung menekan/mengfokus pada sebuah elemen, contoh pada sebuah `<input>`.\n\n**Peristiwa dokumen (_Document events_):**\n- `DOMContentLoaded` -- pada saat HTML telah dimuat dan diproses, DOM telah sepenuhnya dibuat.\n\n**Peristiwa CSS (_CSS events_):**\n- `transitionend` -- pada saat animasi CSS selesai.\n\nMasih banyak lagi peristiwa lain. Kita akan membahas lebih detail tentang peristiwa tertentu pada bab selanjutnya.\n\n## Penangan peristiwa (_Event handlers_)\n\nUntuk menanggapi sebuah perristiwa kita dapat membuat penangan -- sebuah fungsi yang akan dijalankan pada saat peristiwa itu terjadi.\n\npenangan adalah sebuah cara untuk menjalankan kode Javascript pada saat pengguna melakukan sesuatu.\n\nAda banyak cara untuk membuat sebuah handler. Mari kita pelajari, dimulai dari yang paling sederhana.\n\n### Atribut HTML (_HTML-attribute_)\n\nSebuah penangan bisa di atur pada HTML dengan menggunakan atribute `on<event>`.\n\nContohnya, untuk mengatur sebuah penangan `klik` untuk `input`, kita bisa gunakan `onclick`, seperti ini:\n\n```html run\n<input value=\"Klik saya\" *!*onclick=\"alert('Klik!')\"*/!* type=\"button\">\n```\n\nPada klik mouse, kode didalam `onclick` dijalankan.\n\nHarap di catat bahwa didalam `onclick` kita gunakan tanda kutipan tunggal (_single quotes_), karena atribute itu sendiri menggunakan tanda kutip ganda (_double quotes_). Jika lupa bahwa kode tersebut didalam atribut dan menggunakan tanda kutip ganda (_double quotes_), seperti ini: `onclick=\"alert(\"Klik!\")\"`, maka itu tidak akan bekerja dengan benar.\n\nSebuah atribute-HTML bukan tempat yang cocok untuk menulis banyak kode, jadi kita buat sebuah fungsi Javascript dan memanggilnya disana.\n\nSebuah kilk menjalankan sebuah fungsi `hitungKelinci()`:\n\n```html autorun height=50\n<script>\n  function hitungKelinci() {\n    for(let i=1; i<=3; i++) {\n      alert(\"Kelinci nomor \" + i);\n    }\n  }\n</script>\n\n<input type=\"button\" *!*onclick=\"hitungKelinci()\"*/!* value=\"Hitung Kelinci!\">\n```\n\nSeperti yang kita ketahui, atribut HTML tidak _case-sensitive_, jadi `ONCLICK`, `onClick` dan `onCLICK` bisa digunakan... Tapi biasanya atribut menggunakan huruf kecil: `onclick`.\n\n### Properti DOM (_DOM property_)\n\nSebuah penangan bisa di atur menggunakan properti DOM`on<event>`.\n\nContohnya, `elem.onclick`:\n\n```html autorun\n<input id=\"elem\" type=\"button\" value=\"Klik saya\">\n<script>\n*!*\n  elem.onclick = function() {\n    alert('Terima Kasih');\n  };\n*/!*\n</script>\n```\n\nJika penangan di atur menggunakan atribut-HTML maka peramban membaca, membuat sebuah fungsi baru dari konten atribute dan menulisnya pada properti DOM.\n\nJadi cara ini sebenarnya sama dengan yang sebelumnya.\n\nKedua kode ini memiliki cara kerja yang sama:\n\n1. Hanya HTML:\n\n    ```html autorun height=50\n    <input type=\"button\" *!*onclick=\"alert('Klik!')\"*/!* value=\"Tombol\">\n    ```\n2. HTML + JS:\n\n    ```html autorun height=50\n    <input type=\"button\" id=\"button\" value=\"Tombol\">\n    <script>\n    *!*\n      button.onclick = function() {\n        alert('Klik!');\n      };\n    */!*\n    </script>\n    ```\n\nPada contoh pertama, atribut HTML digunakan untuk menginisialisasikan `tombol.onclick`, sedangkan pada contoh kedua -- _script_, dan hanya itu perbedaanya.\n\n**Karena hanya ada satu properti `onclick`, kita tidak bisa mengatur lebih dari satu penangan peristiwa.**\n\nPada contoh dibawah menambah sebuah penangan menggunakan Javascript akan menimpa penangan yang sudah ada:\n\n```html run height=50 autorun\n<input type=\"button\" id=\"elem\" onclick=\"alert('Sebelum')\" value=\"Klik saya\">\n<script>\n*!*\n  elem.onclick = function() { // menimpa penangan yang sudah ada\n    alert('Sesudah'); // hanya ini yang akan ditunjukan\n  };\n*/!*\n</script>\n```\n\nUntuk menghapus sebuah penangan -- atur `elem.onclick = null`\n\n## Mengakses elemen: this\n\nnilai dari `this` didalam penangan adalah elemen tersebut. Elemen yang dimana penangan itu berada.\n\nPada kode dibawah `button` menampilkan kontennya dengan menggunakan `this.innerHTML`:\n\n```html height=50 autorun\n<button onclick=\"alert(this.innerHTML)\">Klik saya</button>\n```\n\n## Kemungkinan kesalahan\n\nJika kamu mulai bekerja dengan menggunakan peristiwa -- harap perhatikan beberapa detail. \n\nKita bisa mengatur sebuah fungsi yang telah ada sebagai penangan:\n\n```js\nfunction ucapkanTerimaKasih() {\n  alert('Terima Kasih!');\n}\n\nelem.onclick = ucapkanTerimaKasih;\n```\n\nTetapi berhati-hatilah: fungsi harus di atur sebagai `ucapkanTerimaKasih`, bukan `ucapkanTerimaKasih()`.\n\n```js\n// benar\nbutton.onclick = ucapkanTerimaKasih;\n\n// salah\nbutton.onclick = ucapkanTerimaKasih();\n```\n\nJika kita tambahkan tanda kurung, maka `ucapkanTerimaKasih()` menjadi proses pemanggilan fungsi. Jadi baris terakhir akan mengambil *hasil* dari pengeksekusian fungsi, yang merupakan `tidak terdefinisi` (_`undefined`_ — karena fungsi tidak mengembalikan apapun), dan mengatur nilai itu ke peristiwa `onclick`. Maka peristiwa tersebut tidak akan menjalankan apapun.\n\n...Namun, jika kita menambahkan secara langsung ke HTML, maka kita harus menambahkan tanda kurung:\n\n```html\n<input type=\"button\" id=\"button\" onclick=\"ucapkanTerimaKasih()\">\n```\n\nPerbedaannya mudah untuk di jelaskan. Pada saat peramban membaca atribute, peramban akan membuat fungsi penangan yang didalamnya terdapat konten dari atribut tersebut.\n\nJadi HTML akan menghasilkan properti ini:\n```js\nbutton.onclick = function() {\n*!*\n  ucapkanTerimaKasih(); // <-- konten dari atribut akan ditambahkan kesini\n*/!*\n};\n```\n\n**Jangan gunakna `setAttribute` untuk membuat penangan.**\n\nPenggunaan tersebut tidak akan berjalan:\n\n```js run no-beautify\n// sebuah klik pada <body> akan menghasilakn eror\n// karena atribute akan selalu menjadi teks (string), dimana fungsi akan menjadi teks (string)\ndocument.body.setAttribute('onclick', function() { alert(1) });\n```\n\n**Properti DOM mementingkan kesamaan huruf.**\n\nAtur sebuah penangan ke `elem.onclick`, bukan `elem.ONCLICK`, karena properti DOM mementingkan kesamaan huruf (_case-sensitive_).\n\n## tambahkanPendengarPeristiwa (_addEventListener_)\n\nSalah satu masalah mendasar pada cara mengatur pengedali sebelumnya -- kita tidak bisa mengatur beberapa penangan pada sebuah peristiwa.\n\nMari kata, sebuah bagian pada koded kita ingin menyoroti sebuah tombol pada saat diklik, dan satu lagi ingin menunjukan seubah pesan pada proses pengklikan tersebut.\n\nKita ingin mengatur dua penangan peristiwa untuk hal tersebut. Tapi properti DOM yang baru akan menimpa properti DOM yang telah ada.\n\n```js no-beautify\ninput.onclick = function() { alert(1); }\n// ...\ninput.onclick = function() { alert(2); } // menganti pengedali yang lama\n```\n\nPengembang dari standar situs web paham sejak lama, dan menyarankan cara alternatif untuk mengelola penangan menggunakan metode khusus `addEventListener` dan `removeEventListener`. Kedua hal tersebut tidak memiliki permasalahan seperti itu.\n\nSintaks (_syntax_) untuk menambahkan sebuah penangan:\n\n```js\nelement.addEventListener(event, handler, [options]);\n```\n\n`peristiwa`/`event`\n: nama Peristiwa, contoh `\"click\"`.\n\n`penangan`/`handler`\n: penangan fungsi.\n\n`pilihan`/`options`\n: sebuah objek pilihan tambahan dengan properti:\n    - `once`: jika `true`, maka pendengar akan secara otomatis dihapus setelah terpicu.\n    - `capture`: fase dimana untuk menangani peristiwa, akan di bahas lebih lanjut pada bab <info:bubbling-and-capturing>. untuk alasan sejarah, `options` bisa juga diatur `false/true`, sama halnya dengan `{capture: false/true}`.\n    - `passive`: jika `true`, maka penangan tidak akan memanggil `preventDefault()`, kita akan membahas lebih lanjut pada bab <info:default-browser-action>.\n\nUntuk menghapus penangan, gunakan `removeEventListener`:\n\n```js\nelement.removeEventListener(event, handler, [options]);\n```\n\n````warn header=\"Penghapusan membutuhkan fungsi yang sama\"\nUntuk menghapus sebuah penangan kita melewatkan fungsi yang sama dengan yang kita atur.\n\nIni tidak akan berfungsi:\n\n```js no-beautify\nelem.addEventListener( \"click\" , () => alert('Terima Kasih!'));\n// ....\nelem.removeEventListener( \"click\", () => alert('Terima Kasih!'));\n```\n\nPengedali tidak akan dihapus, karena `removeEventListener` mendapat sebuah fungsi lain -- dengan kode yang sama, tetapi hal tersebut tidak penting, karena itu merupakan objek fungsi yang berbeda.\n\nInilah cara yang benar:\n\n```js\nfunction handler() {\n  alert( 'Terima Kasih!' );\n}\n\ninput.addEventListener(\"click\", handler);\n// ....\ninput.removeEventListener(\"click\", handler);\n```\n\nHarap dicatat -- Jika kita tidak menyimpan fungsi tersebut kedalam variable, maka kita tidak bisa menghapusnya. Tidak ada cara untuk \"membaca kembali\" penangan yang di atur pada `addEventListener`.\n````\n\nBeberapa pemanggilan ke `addEventListener` mengijinkan untuk menambahkan beberapa penangan, seperti ini:\n\n```html run no-beautify\n<input id=\"elem\" type=\"button\" value=\"Klik saya\"/>\n\n<script>\n  function penangan1() {\n    alert('Terima Kasih!');\n  };\n\n  function penangan2() {\n    alert('Terima Kasih lagi!');\n  }\n\n*!*\n  elem.onclick = () => alert(\"Halo\");\n  elem.addEventListener(\"click\", penangan1); // Terima Kasih!\n  elem.addEventListener(\"click\", penangan2); // Terima Kasih lagi!\n*/!*\n</script>\n```\n\nSeperti yang bisa kita lihat pada contoh di atas, kita bisa mengatur *kedua* penangan menggunakan properti DOM dan `addEventListener`. Tapi pada umumnya kita hanya akan menggunakan salah satu.\n\n````warn header=\"Untuk beberapa peristiwa, penangan bekerja hanya dengan `addEventListener`\"\nAda beberapa peristiwa yang tidak dapat di atur menggunakan properti DOM. hanya dengan `addEventListener`.\n\nContohnya, peristiwa `DOMContentLoaded`, yang akan terpicu pada saat dokumen telah berhasil di dimuat dan dibuat.\n\n```js\n// tidak akan perna berjalan\ndocument.onDOMContentLoaded = function() {\n  alert(\"DOM dibuat\");\n};\n```\n\n```js\n// akan berjalan dengan cara ini\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n  alert(\"DOM dibuat\");\n});\n```\nJadi `addEventListener` lebih universal. Walaupun, aturan semacam itu merupakan sebuah pengecualian daripada aturan. \n````\n\n## Objek peristiwa (Event object)\n\nUntuk menangani peristiwa secara benar sebuah peristiwa kita mau tahu lebih tentang apa yang terjadi. Tidak hanya sebuah \"klik\" atau sebuah \"penekanan tombol\", tapi apa koordinat pointer? tombol mana yang di tekan? dan seterusnya.\n\nPada saat sebuah peristiwa terjadi, peramban akan membuat *objek peristiwa*, memasukan detail kedalamnya dan meneruskan peristiwa tersebut ke penangan sebagai sebuah argumen.\n\nIni merupakan contoh cara untuk mendapat koordinat pointer dari objek peristiwa:\n\n```html run\n<input type=\"button\" value=\"Klik saya\" id=\"elem\">\n\n<script>\n  elem.onclick = function(*!*peristiwa*/!*) {\n    // tampilkan tipe peristiwa, elemen dan koordinat dari klik\n    alert(peristiwa.type + \" pada \" + peristiwa.currentTarget);\n    alert(\"Koordinat: \" + peristiwa.clientX + \":\" + peristiwa.clientY);\n  };\n</script>\n```\n\nBeberapa properti dari objek `peristiwa`:\n\n`peristiwa.type`\n: Tipe peristiwa, disini tipenya `\"click\"`.\n\n`peristiwa.currentTarget`\n: elemen yang ditangani oleh peristiwa. Sama persis dengan `this`, kecuali jika penangan merupakan fungsi anak panah (arrow function), atau `this` sudah di atur untuk hal lain, maka kita dapat menggunakan `peristiwa.currentTarget` untuk mendapati elemen.\n\n`peristiwa.clientX / peristiwa.clientY`\n: koordinat kursor relatif pada jendela (window), untuk peristwa pointer.\n\nMasih banyak lagi properti. Banyak yang tergantung pada tipe peristiwa: peristiwa papan ketik memilik satu set properti, peristiwa pointer - memiliki set yang berbeda, kita nanti akan mempelajari mereka pada saat kita mendapati peristiwa lainnya secara detail. \n\n````smart header=\"Objek peristiwa juga ada pada penangan HTML\"\nJika kita mengatur penangan pada HTML, kita bisa juga menggunakan objek peristiwa, seperti ini:\n\n```html autorun height=60\n<input type=\"button\" onclick=\"*!*alert(event.type)*/!*\" value=\"Tipe Peristiwa\">\n```\n\nIni terjadi karena pada saat peramban membaca atribut, itu membuat sebuah penangan seperti ini: `function(event) {alert(event.type) }`. yaitu: argumen pertamanya disebut dengan `\"event\"`, dan tubuhnya di ambil dari atribut.\n````\n\n\n## Objek penangan: handleEvent\n\nKita bisa mengatur bukan hanya fungsi, tapi sebuah objek sebagai penangan peristiwa menggunakan `addEventListener`. Pada saat sebuah peristiwa terjadi, Itu memanggil metode `handleEvent`. \n\nContohnya:\n\n\n```html run\n<button id=\"elem\">Klik saya</button>\n\n<script>\n  let obj = {\n    handleEvent(event) {\n      alert(event.type + \" pada \" + event.currentTarget);\n    }\n  };\n\n  elem.addEventListener('click', obj);\n</script>\n```\n\nSeperti yang bisa kita lihat, pada saat `addEventListener` menerima objek sebagai penangan, itu akan memanggil `obj.handleEvent(event)` jika sebuah peristiwa terjadi.\n\nKita juga dapat menggunakan Kelas (_class_) untuk hal itu:\n\n\n```html run\n<button id=\"elem\">Klik saya</button>\n\n<script>\n  class Menu {\n    handleEvent(event) {\n      switch(event.type) {\n        case 'mousedown':\n          elem.innerHTML = \"Tombol mouse ditekan\";\n          break;\n        case 'mouseup':\n          elem.innerHTML += \"...dan dilepas.\";\n          break;\n      }\n    }\n  }\n\n*!*\n  let menu = new Menu();\n  elem.addEventListener('mousedown', menu);\n  elem.addEventListener('mouseup', menu);\n*/!*\n</script>\n```\n\nDisini objek yang sama menanggani kedua peristiwa. Tolong di catat bahwa kita harus secara eksplisit mengatur peristiwa untuk mendengar menggunakan `addEventListener`. Objek `menu` hanya dapat menerima `mousedown` dan `mouseup` pada contoh diatas, bukan tipe peristiwa lainnya.\n\nMetode `handleEvent` tidak harus melakukan semua proses secara mandiri. Itu dapat memanggil metode lain yang menanggani peristiwa secara spesifik, seperti ini:\n\n```html run\n<button id=\"elem\">Klik saya</button>\n\n<script>\n  class Menu {\n    handleEvent(event) {\n      // mousedown -> onMousedown\n      let method = 'on' + event.type[0].toUpperCase() + event.type.slice(1);\n      this[method](event);\n    }\n\n    onMousedown() {\n      elem.innerHTML = \"Tombol mouse ditekan\";\n    }\n\n    onMouseup() {\n      elem.innerHTML += \"...dan dilepas.\";\n    }\n  }\n\n  let menu = new Menu();\n  elem.addEventListener('mousedown', menu);\n  elem.addEventListener('mouseup', menu);\n</script>\n```\n\nSekarang penangan peristiwa berbeda, dan memudahkan proses pendukungan.\n\n## Ringkasan\n\nAda 3 cara untuk mengatur penangan peristiwa:\n\n1. Atribute HTML: `onclick=\"...\"`\n2. Properti DOM: `elem.onclick = function`.\n3. Metode: `elem.addEventListener(event, handler[, phase])` untuk menambahkan, `removeEventListener` untuk menghapuskan.\n\nAtribute HTML digunakan untuk kasus tertentu, karena Javascript ditengah tag HTML akan kelihatan aneh. Dan juga akan sulit untuk menulis banyak kode di dalam tag HTML.\n\nProperti DOM boleh digunakan, tapi kita tidak dapat mengatur lebih dari 1 penangan untuk peristiwa tertentu. Namun tidak sering kita membutuhkan lebih dari 2 penangan.\n\nCara terakhir lebih fleksible, tapi itu juga merupakan cara terpanjang untuk menulis. Ada beberapa peristiwa yang hanya akan bisa digunakan pada cara ini, seperti misalnya `transitionend` dan `DOMContentLoaded` (akan di bahas). Dan juga objek dapat digunakan sebagai penangan pada `addEventListener`. Pada kasus ini metode `handleEvent` akan dipanggil pada saat peristiwa terjadi.\n\nTidak penting bagaimana kamu mengatur penangan -- itu akan mendapat sebuah objek peristiwa sebagai argumen pertama. Objek itu memiliki detail tentang apa yang terjadi.\n\nKita akan mempelajari lebih lanjut tentang peristiwa secara umum dan perbedaan tipe peristiwa di bab selanjutnya.\n"
  },
  {
    "path": "2-ui/2-events/01-introduction-browser-events/head.html",
    "content": "<style>\n/*\n.d0 { text-align:center;margin:auto; }\n.d1 p { margin: 0 }\n.d1 {\nmargin:2em;\nbackground-color:green;\nwidth:13em;\nheight:13em;\ntext-align:center;\n}\n.d1 .number {\n  line-height: 2em;\n}\n.d2 {\ntext-align:center;\nmargin:auto;\nbackground-color:blue;\nwidth:9em;\nheight:9em;\n}\n.d1 .d2 ,number {\n  line-height: 2em;\n}\n.d3 {\ntext-align:center;\nmargin:auto;\nbackground-color:red;\nwidth:5em;\nheight:5em;\n}\n.d1 .d2 .d3 .number {\n  line-height: 5em;\n}\n.d1 .d2 .d2a {\n  color:white;\n  line-height: 2em;\n}\n*/\n</style>\n<script>\n/*\nfunction highlightMe(elem) {\n    elem.style.backgroundColor='yellow'\n    alert(elem.className)\n    elem.style.backgroundColor = ''\n}\n\nfunction highlightMe2(e) {\n    highlightMe(e.currentTarget);\n}\n*/\n</script>\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/article.md",
    "content": "# Menggelembung (_bubbling_) dan menangkap (_capturing_)\n\nAyo mulai dengan sebuah contoh.\n\nSebuah penangan (_handler_) di atur ke `<div>`, tapi juga dijalankan jika kita klik salah satu tag bawaan seperti `<em>` or `<code>`:\n\n```html autorun height=60\n<div onclick=\"alert('Penangan (handler)!')\">\n  <em>Jika kamu menekan pada <code>EM</code>, penangan pada <code>DIV</code> akan berjalan.</em>\n</div>\n```\n\nBukan kah itu sedikit aneh? kenapa penangan (_handler_) pada `<div>` berjalan padahal elemen yang di klik adalah `<em>`?\n\n## Menggelembung (_bubbling_)\n\nPrinsip menggelembung (_bubbling_) itu sederhana.\n\n**Pada saat sebuah peristiwa terjadi ke sebuah elemen, peristiwa itu akan menjalankan penangan (_handler_) yang ada pada elemen itu, kemudian pada elemen orang tua (_parent_), dan seterusnya hingga sampai ke elemen yang paling atas (_ancestors_).**\n\nBayangkan kita memiliki tiga elemen bersarang `FORM > DIV > P` dengan penagan (_handler_) pada setiap elemen:\n\n```html run autorun\n<style>\n  body * {\n    margin: 10px;\n    border: 1px solid blue;\n  }\n</style>\n\n<form onclick=\"alert('form')\">FORM\n  <div onclick=\"alert('div')\">DIV\n    <p onclick=\"alert('p')\">P</p>\n  </div>\n</form>\n```\nSebuah klik pada bagian dalam `<p>` akan menjalankan `onclick`:\n1. Yang ada pada `<p>`.\n2. Kemudian pada `<div>`.\n3. Kemudian pada `<form>`.\n4. Dan seterusnya hingga sampai ke objek `document`.\n\n![](event-order-bubbling.svg)\n\nJadi jika kita klik pada `<p>`, kemudian kita akan melihat 3 buah peringatan (_alerts_): `p` -> `div` -> `form`.\n\nProses ini disebut dengan \"menggelembung (_bubbling_)\", karena peristiwa akan \"mengelembung (_bubble_)\" dari bagian dalam elemen ke atas melalui elemen orang tua (_parents_) seperti sebuah gelembung di air.\n\n```warn header=\"*Hampir* semua peristiwa bergelembung.\"\nKata kunci pada kata tersebut adalah \"hampir\".\n\nContohnya, peristiwa `focus` tidak bergelembung. Masih ada contoh lain, kita akan membahas mereka. Tapi tetap itu hanya pengecualian, dan bukan aturan baku, hampir semua peristiwa mengelembung.\n```\n\n## event.target\n\nSebuah penangan (_handler_) pada elemen orang tua bisa selalu mendapat detail tentang dimana kejadian itu terjadi.\n\n**elemen bersarang yang mengakibatkan peristiwa (_event_) di panggil di sebut sebuah *target* elemen, diakses dengan menggunakan `event.target`.**\n\nCatat perbedaan dari `this` (=`event.currentTarget`):\n\n- `event.target` -- adalah \"target\" elemen yang menginisialisasi peristiwa (_event_), dan tidak berubah pada proses pengelembungan.\n- `this` -- adalah elemen tersebut, elemen yang sedang menjalankan penangan (_handler_).\n\nContohnya, jika sebuah penangan (_handler_) `form.onclick`, kemudian form itu akan \"menangkap\" semua klik yang terjadi didalam form. Tidak peduli dimana klik itu terjadi, klik itu akan mengelembung ke `<form>` dan akan menjalankan penangan (_handler_).\n\nPada penangan (_handler_) `form.onclick`:\n\n- `this` (=`event.currentTarget`) adalah elemen `<form>`, karena penangan (_handler_) dijalankan pada `<form>`.\n- `event.target` adalah elemen didalam `<form>` dimana peristiwa klik terjadi.\n\nContohnya:\n\n[codetabs height=220 src=\"bubble-target\"]\n\n`this` dan `event.target` bisa merupakan elemen yang sama -- itu terjadi pada saat klik terjadi tepat di elemen `<form>`.\n\n## Menghentikan Menggelembung (_bubbling_)\n\nSebuah proses menggelembung berasal dari `target` elemen akan naik keatas. Biasanya proses itu akan terjadi sampai mencapai elemen `<html>`, dan kemudian ke objek `document`, dan ada beberapa peristiwa (_event_) yang bahkan bisa mencapai jendela (_`window`_), sambil menjalankan semua penangan (_handler_) yang ada di setiap elemen.\n\nTapi salah satu penangan (_handler_) dapat menghentikan peristiwa (_event_) jika penangan (_handler_) beranggapan bahwa proses tersebut telah berhasil di proses.\n\nMetode untuk melakukan perhentian adalah `event.stpoPropagation()`.\n\nContohnya, `body.onclick` tidak akan dijalankan jika kamu mengklik pada `<button>`:\n\n```html run autorun height=60\n<body onclick=\"alert(`Proses menggelembung tidak mencapai penangan ini`)\">\n  <button onclick=\"event.stopPropagation()\">Klik saya</button>\n</body>\n```\n\n```smart header=\"event.stopImmediatePropagation()\"\nJika sebuah elemen memiliki beberapa penangan (handler) untuk satu peristiwa (event), maka bahkan jika salah satu dari penangan menghentikan proses pengelembungan, penagan yang lain akan tetap di jalankan.\n\nDengan kata lain, `event.stopPropagation()` menghentikan proses yang keatas, tapi pada elemen yang sama penangan (handler) lain akan tetap di jalankan.\n\nUntuk menghentukan pengelembungan (handler) dan mencegah penangan (handler) lain yang ada pada elemen tersebut untuk dijalankan, harus menggunakan metode `event.stopImmediatePropagation()`. Setelah itu tidak akan ada penangan (handler) yang dijalankan.\n```\n\n```warn header=\"Jangan menghentikan proses mengelembung jika tidak perlu!\"\nProses mengelembung cukup berguna. Jangan menghentikan proses ini jika tidak ada perlu: tentu saja harus di pikir dengan baik-baik.\n\nTerkadang `event.stopPropagation()` akan menyebabkan jebakan tersembunyi yang mungkin akan menjadi masalah.\n\nContoh:\n\n1. Kita membuat sebuah menu yang bersarang. Pada setiap submenu penangan (_handles_) klik pada elemen itu dan menjalankan `stopPropagation` jadi bagian luar menu tidak akan dijalankan.\n2. Kemudian kita memutuskan untuk menangkap klik pada keseluruhan jendela (_window_), untuk melacak kebiasaan pengguna (dimana biasa pengguna mengklik). Beberapa sistem analisa menggunakan metode ini. Biasanya code yang digunakan `document.addEventListener('click'…)` untuk menangkap semua klik.\n3. Analisis kita tidak akan bekerja pada area dimana kita telah menghentikan peristiwa klik dengan menggunakan `stopPropagation`. Dengan kata lain kita telah membuat daerah mati (_dead zone_).\n\nBiasanya tidak ada keperluan utama yang membuat kita harus menghentikan proses mengelembung. Sebuah fungsi yang kelihatannya membutuhkan penggunaan metode itu bisa di selesaikan dengan menggunakan cara lain. Salah satunya dengan menggunakan peristiwa khusus, kita akan membahasnya nanti. Dan juga kita dapat menulis data kedalam objek `event` pada sebuah penangan (handler) dan membacanya pada penangan (handler) lainnya, jadi kita dapat meneruskan data tentang proses yang terjadi dibawah ke penangan (handler) elemen atas.\n```\n\n\n## Penangkapan (_Capturing_)\n\nAda juga sebuah fase pada proses peristiwa yang disebut dengan Penangkapan (_capturing_). proses ini jarang digunakan, tapi akan berguna pada saat dibutuhkan.\n\nStandar sebuah [Peristiwa DOM](http://www.w3.org/TR/DOM-Level-3-Events/) terbagi menjadi 3 fase, yaitu:\n\n1. fase penangkapan (_capturing phase_) -- peristiwa mulai mencari elemen.\n2. fase target (_target phase_) -- peristiwa menemukan elemen.\n3. fase mengelembung (_bubbling phase_) -- peristiwa mulai naik ke atas dari elemen dasar.\n\nBerikut ini sebuah gambar tentang klik yang terjadi pada `<td>` didalam sebuah tabel, yang diambil dari spesifikasi:\n\n![](eventflow.svg)\n\nMaka: untuk klik pada `<td>` peristiwa (_event_) akan pertama melewati elemen paling atas dan turun ke elemen yang bawaha (fase penangkapan), kemudian pada saat mencapai elemen yang di target akan di jalankan pada elemen tersebut (fase target), dan kemudia peristiwa itu akan naik ke atas (fase mengelembung), sambil memanggil penangan (_handler_) yang ada.\n\n**Sebelumnya kita hanya membahas tentang proses pengelembungan, karena proses penangkapan jarang digunakan, biasanya proses ini tidak terlihat oleh kita.**\n\nPenangan (_Handlers_) yang di tambahkan menggunakan `on<event>`-properti atau menggunakan atribut HTML atau menggunakan dua argumen `addEventListener(event, handler)` tidak mengetahui tentang proses penangkapan, mereka hanya menjalankan fase ke 2 dan fase ke 3.\n\nUntuk menangkap sebuah peristiwa pada fase penangkapan, kita perlu mengatur penangan (_handler_) pilihan `capture` menjadi `true`:\n\n```js\nelem.addEventListener(..., {capture: true})\n// atau, hanya \"true\" karena merupakan alias dari {capture: true}\nelem.addEventListener(..., true)\n```\n\nHanya ada 2 kemungkinan nilai dari pilihan `capture`:\n\n- Jika `false` (bawaan (_default_) ), maka penangan (_handler_) di atur pada fase pengelembungan atau fase ke 3.\n- Jika `true`, aka penangan (_handler_) di atur pada fase penangkapan atau fase pertama.\n\nCatatan, sementara secara umum hanya ada 3 fase, dan fase ke dua (\"fase target\": peristiwa mencapai elemen yang di target) tidak di tangani secara terpisah: penangan (_handler_) pada kedua fase penangkapan dan pengelembungan di jalankan pada fase tersebut.\n\nMari lihat kedua fase penangkapan dan pengelembungan:\n\n```html run autorun height=140 edit\n<style>\n  body * {\n    margin: 10px;\n    border: 1px solid blue;\n  }\n</style>\n\n<form>FORM\n  <div>DIV\n    <p>P</p>\n  </div>\n</form>\n\n<script>\n  for(let elem of document.querySelectorAll('*')) {\n    elem.addEventListener(\"click\", e => alert(`Penangkapan: ${elem.tagName}`), true);\n    elem.addEventListener(\"click\", e => alert(`Pengelembungan: ${elem.tagName}`));\n  }\n</script>\n```\n\nKode mengatur penangan(_handler_) klik pada *setiap* elemen yang ada di dalam dokumen untuk melihat elemen mana yang berfungsi.\n\nJika kamu klik pada `<p>`, maka rangkaian peristiwa sebagai berikut:\n\n1. `HTML` -> `BODY` -> `FORM` -> `DIV` (fase penangkapan, pendengar pertama),\n2. `P` (fase target, dijalankan 2 kali, karena kita mengatur 2 pendengar: penangkapan dan pengelembungan),\n3. `DIV` -> `FORM` -> `BODY` -> `HTML` (fase pengelembungan, pendengar kedua).\n\nAda sebuah properti `event.eventPhase` yang akan memberikan kita nomor dari fase yang dimana peristiwa tersebut di tangkap. Tapi properti ini jarang digunakan, karena kita biasanya mendapat info itu dari penangan(_handler_) itu sendiri.\n\n```smart header=\"Untuk menghapus penangan, `removeEventListener` membutuhkan fase yang sama\"\nJika kita menggunakan `addEventListener(..., true)`, maka kita harus menggunakan fase yang sama pada `removeEventListener(..., true)` untuk menghapus penangan secara benar.\n```\n\n````smart header=\"Pendengar pada elemen dan fase yang sama akan dijalankan berdasarkan urutan mereka\"\nJika kita memiliki beberapa penangan pada fase yang sama, dan di atur pada elemen yang sama dengan menggunakan `addEventListener`, mereka akan berjalan sesuai dengan urutan mereka di buat:\n\n```js\nelem.addEventListener(\"click\", e => alert(1)); // akan selalu berjalan duluan\nelem.addEventListener(\"click\", e => alert(2));\n```\n````\n\n## Ringkasan\n\nPada saat sebuah peristiwa (_event_) terjadi -- elemen yang paling dalam dimana peristiwa itu terjadi akan di tandai dengan label \"target elemen\" (`event.target`).\n\n- Kemudian peristiwa akan turun kebawah dari akar dokumen ke `event.target`, memanggil penangan yang di atur dengan `addEventListener(...,true)` (`true` kependekan dari `{capture: true}`).\n- Kemudian penangan akan di panggil pada target elemen itu sendiri.\n- Kemudian peristiwa akan naik ekatas dari `event.target` ke akar dokumen, memanggil penangan yang di ataur menggunakan `on<event>`, atribut HTML dan `addEventListener` tanpa argumen ke tiga atau dengan `false/{capture:false}.\n\nSetiap penangan(_handler_) memiliki akses ke properti objek `event`:\n\n- `event.target` -- elemen paling bawah dimana peristiwa itu terjadi.\n- `event.currentTarget` (=`this`) -- merupakan elemen yang menangani peristiwa (elemen yang memiliki penangan (_handler_)).\n- `event.eventPhase` -- fase yang sedang terjadi (penangkapan=1, target=2, pengelembungan=3).\n\nPenangan dapat menghentikan peristiwa dengan memanggil `event.stopPropagation()`, tapi tidak direkomendasikan, karena kita tidak belum tentu tidak memerlukan peristiwa itu pada elemen di atas, mungkin untuk hal yang berbeda.\n\nFase penangkapan jarang digunakan, biasanya kita menangani peristiwa yang mengelembung. Dan ada logika dibaliknya.\n\nPada dunianya, pada saat kecelakaan terjadi, petugas setempat akan bereaksi duluan. Mereka lebih mengetahui daerah dimana kejadian itu terjadi. Kemudian petugas yang bertingkat tinggil jika dibutuhkan.\n\nHal yang sama juga untuk penanganan peristiwa. Kode yang mengatur penangan (_handler_) pada elemen tertentu mengetahui dengan maksimum rincian tentang elemen tersebut dan apa yang harus dilakukan. Sebuah penangan (_handler_) pada `<td>` mungkin lebih cocok untuk `<td>`, penangan itu mengetahui segalanya, jadi penangan itu harus dijalankan duluan. Kemudian elemen yang diatasnya mengetahui tentang konteksnya, mungkin lebih sedikit, dan seterusnya sampai pada elemen yang paling atas, yang mengatur tentang konsep secara umum dan dijalankan paling akhir.\n\nPengelembungan dan Penangkapan menyediakan sebuah fondasi untuk \"event delegation\" -- sebuah pola penanganan peristiwa yang cukup penting yang akan kita pelajari pada bab selanjutnya.\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n</head>\n\n<body>\n\n  <form>FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/both.view/script.js",
    "content": "let elems = document.querySelectorAll('form,div,p');\n\nfor (let i = 0; i < elems.length; i++) {\n  elems[i].addEventListener(\"click\", highlightThis, true);\n  elems[i].addEventListener(\"click\", highlightThis, false);\n}\n\nfunction highlightThis() {\n  this.style.backgroundColor = 'yellow';\n  alert(this.tagName);\n  this.style.backgroundColor = '';\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"example.css\">\n</head>\n\n<body>\n  Sebuah klik untuk menunjukan kedua <code>event.target</code> dan <code>this</code> untuk dibandingkan:\n\n  <form id=\"form\">FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/bubble-target.view/script.js",
    "content": "\nform.onclick = function(event) {\n  event.target.style.backgroundColor = 'yellow';\n\n  // chrome membutuhkan waktu untuk mewarnai warna kuning.\n  setTimeout(() => {\n    alert(\"target = \" + event.target.tagName + \", this=\" + this.tagName);\n    event.target.style.backgroundColor = ''\n  }, 0);\n};\n"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/example.css",
    "content": "form {\n  background-color: green;\n  position: relative;\n  width: 150px;\n  height: 150px;\n  text-align: center;\n  cursor: pointer;\n}\n\ndiv {\n  background-color: blue;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 100px;\n  height: 100px;\n}\n\np {\n  background-color: red;\n  position: absolute;\n  top: 25px;\n  left: 25px;\n  width: 50px;\n  height: 50px;\n  line-height: 50px;\n  margin: 0;\n}\n\nbody {\n  line-height: 25px;\n  font-size: 16px;\n}"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"example.css\">\n\n  <form>FORM\n    <div>DIV\n      <p>P</p>\n    </div>\n  </form>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/02-bubbling-and-capturing/capture.view/script.js",
    "content": "let elems = document.querySelectorAll('form,div,p');\n\nfor (let i = 0; i < elems.length; i++) {\n  elems[i].addEventListener(\"click\", highlightThis, true);\n}\n\nfunction highlightThis() {\n  this.style.backgroundColor = 'yellow';\n  alert(this.tagName);\n  this.style.backgroundColor = '';\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"pane\">\n      <h3>Kuda</h3>\n      <p>Kuda adalah salah satu dari dua subspesies Equus ferus yang masih ada. Ini adalah mamalia berkuku aneh milik keluarga taksonomi Equidae. Kuda telah berevolusi selama 45 hingga 55 juta tahun terakhir dari makhluk kecil berjari banyak, Eohippus, menjadi hewan besar berjari satu saat ini.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Keledai</h3>\n      <p>Keledai (Equus africanus asinus) adalah anggota keluarga kuda yang dijinakkan, Equidae. Nenek moyang keledai liar adalah keledai liar Afrika, E. africanus. Keledai telah digunakan sebagai hewan pekerja setidaknya selama 5000 tahun.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Kucing</h3>\n      <p>Kucing domestik (bahasa Latin: Felis catus) adalah mamalia karnivora kecil yang biasanya berbulu. Mereka sering disebut kucing rumah ketika dipelihara sebagai hewan peliharaan dalam ruangan atau hanya kucing ketika tidak perlu membedakan mereka dari felids dan kucing lainnya. Kucing sering dihargai oleh manusia karena persahabatan dan kemampuan mereka untuk berburu hama.\n      </p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n  </div>\n\n  <script>\n    container.onclick = function(event) {\n      if (event.target.className != 'remove-button') return;\n\n      let pane = event.target.closest('.pane');\n      pane.remove();\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/solution.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link rel=\"stylesheet\" href=\"messages.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"pane\">\n      <h3>Kuda</h3>\n      <p>Kuda adalah salah satu dari dua subspesies Equus ferus yang masih ada. Ini adalah mamalia berkuku aneh milik keluarga taksonomi Equidae. Kuda telah berevolusi selama 45 hingga 55 juta tahun terakhir dari makhluk kecil berjari banyak, Eohippus, menjadi hewan besar berjari satu saat ini.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Keledai</h3>\n      <p>Keledai (Equus africanus asinus) adalah anggota keluarga kuda yang dijinakkan, Equidae. Nenek moyang keledai liar adalah keledai liar Afrika, E. africanus. Keledai telah digunakan sebagai hewan pekerja setidaknya selama 5000 tahun.</p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n    <div class=\"pane\">\n      <h3>Kucing</h3>\n      <p>Kucing domestik (bahasa Latin: Felis catus) adalah mamalia karnivora kecil yang biasanya berbulu. Mereka sering disebut kucing rumah ketika dipelihara sebagai hewan peliharaan dalam ruangan atau hanya kucing ketika tidak perlu membedakan mereka dari felids dan kucing lainnya. Kucing sering dihargai oleh manusia karena persahabatan dan kemampuan mereka untuk berburu hama.\n      </p>\n      <button class=\"remove-button\">[x]</button>\n    </div>\n  </div>\n\n  <script>\n    // ...kode kamu...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/source.view/messages.css",
    "content": "body {\n  margin: 10px auto;\n  width: 470px;\n}\n\nh3 {\n  margin: 0;\n  padding-bottom: .3em;\n  padding-right: 20px;\n  font-size: 1.1em;\n}\n\np {\n  margin: 0;\n  padding: 0 0 .5em;\n}\n\n.pane {\n  background: #edf5e1;\n  padding: 10px 20px 10px;\n  border-top: solid 2px #c4df9b;\n  position: relative;\n}\n\n.remove-button {\n  position: absolute;\n  font-size: 110%;\n  top: 0;\n  color: darkred;\n  right: 10px;\n  display: block;\n  width: 24px;\n  height: 24px;\n  border: none;\n  background: transparent;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/1-hide-message-delegate/task.md",
    "content": "importance: 5\n\n---\n\n# Menyembunyikan pesan menggunakan delegasi\n\nAda sebuah daftar dengan tombol `[x]`. Buat tombol itu berfungsi.\n\nSeperti ini:\n\n[iframe src=\"solution\" height=420]\n\nTambahan: Gunakan 1 event listener pada container, gunakan delegasi peristiwa (_event delegation_)."
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/solution.md",
    "content": "Solusi terbagi atas 2 bagian.\n\n1. Bungkus setiap node pada pohon kedalam `<span>`. Kemudian kita bisa menambahkan `:hover` dengan CSS-style dan menanggani klik tepat pada teks, karena lebar `<span>` sama dengan lebar tulisan (lebar tidak sama jika tidak menggunakan `<span>`);  \n2. Atur sebuah penangan (_handler_) ke `tree` akar dari node dan tanggani setiap klik pada judul `<span>`.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    .tree span:hover {\n      font-weight: bold;\n    }\n\n    .tree span {\n      cursor: pointer;\n    }\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul class=\"tree\" id=\"tree\">\n    <li>Hewan\n      <ul>\n        <li>Mamalia\n          <ul>\n            <li>Sapi</li>\n            <li>Keledai</li>\n            <li>Anjing</li>\n            <li>Harimau</li>\n          </ul>\n        </li>\n        <li>Lain\n          <ul>\n            <li>Ular</li>\n            <li>Burung</li>\n            <li>Kadal</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Ikan\n      <ul>\n        <li>Akuarium\n          <ul>\n            <li>Gupi (ikan seribu)</li>\n            <li>Ikan bidadari</li>\n          </ul>\n        </li>\n        <li>Laut\n          <ul>\n            <li>Trout laut</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n  <script>\n    // pindahkan semua teks kedalam <span>\n    // mereka menempati tempat yang tepat diperlukan untuk teks,\n    for (let li of tree.querySelectorAll('li')) {\n      let span = document.createElement('span');\n      li.prepend(span);\n      span.append(span.nextSibling); // memindahkan node teks ke dalam span\n    }\n\n    // tangkap klik pada keseluruhan pohon (tree)\n    tree.onclick = function(event) {\n\n      if (event.target.tagName != 'SPAN') {\n        return;\n      }\n\n      let childrenContainer = event.target.parentNode.querySelector('ul');\n      if (!childrenContainer) return; // tidak ada elemen bawah\n\n      childrenContainer.hidden = !childrenContainer.hidden;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <ul class=\"tree\" id=\"tree\">\n    <li>Hewan\n      <ul>\n        <li>Mamalia\n          <ul>\n            <li>Sapi</li>\n            <li>Keledai</li>\n            <li>Anjing</li>\n            <li>Harimau</li>\n          </ul>\n        </li>\n        <li>Lain\n          <ul>\n            <li>Ular</li>\n            <li>Burung</li>\n            <li>Kadal</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n    <li>Ikan\n      <ul>\n        <li>Akuarium\n          <ul>\n            <li>Gupi (ikan seribu)</li>\n            <li>Ikan bidadari</li>\n          </ul>\n        </li>\n        <li>Laut\n          <ul>\n            <li>Trout laut</li>\n          </ul>\n        </li>\n      </ul>\n    </li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/2-sliding-tree/task.md",
    "content": "importance: 5\n\n---\n\n# Menu pohon (_tree menu_)\n\nBuat sebuah pohon yang akan menampilkan/menyembunyikan elemen bawahan pada saat di klik:\n\n[iframe border=1 src=\"solution\"]\n\nSyarat:\n\n- Hanya satu penangan peristiwa (_event handler_) gunakan delegasi peristiwa.\n- Klik di luar judul node (pada ruang kosong) tidak boleh melakukan apa-apa.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/solution.md",
    "content": "\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    table {\n       border-collapse: collapse;\n     }\n     th, td {\n       border: 1px solid black;\n       padding: 4px;\n     }\n     th {\n       cursor: pointer;\n     }\n     th:hover {\n       background: yellow;\n     }\n  </style>\n</head>\n\n<body>\n\n  <table id=\"grid\">\n    <thead>\n      <tr>\n        <th data-type=\"number\">Umur</th>\n        <th data-type=\"string\">Nama</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>5</td>\n        <td>John</td>\n      </tr>\n      <tr>\n        <td>2</td>\n        <td>Pete</td>\n      </tr>\n      <tr>\n        <td>12</td>\n        <td>Ann</td>\n      </tr>\n      <tr>\n        <td>9</td>\n        <td>Eugene</td>\n      </tr>\n      <tr>\n        <td>1</td>\n        <td>Ilya</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <script>\n\n    grid.onclick = function(e) {\n      if (e.target.tagName != 'TH') return;\n\n      let th = e.target;\n      // Jika TH, maka urutkan\n      // cellIndex adalah nomor dari th:\n      //   0 untuk kolom pertama\n      //   1 untuk kolom kedua, dan seterusnya\n      sortGrid(th.cellIndex, th.dataset.type);\n    };\n\n    function sortGrid(colNum, type) {\n      let tbody = grid.querySelector('tbody');\n\n      let rowsArray = Array.from(tbody.rows);\n\n      // bandingkan(a, b) bandikna kedua baris, untuk di urutkan\n      let compare;\n\n      switch (type) {\n        case 'number':\n          compare = function(rowA, rowB) {\n            return rowA.cells[colNum].innerHTML - rowB.cells[colNum].innerHTML;\n          };\n          break;\n        case 'string':\n          compare = function(rowA, rowB) {\n            return rowA.cells[colNum].innerHTML > rowB.cells[colNum].innerHTML ? 1 : -1;\n          };\n          break;\n      }\n\n      // urutkan\n      rowsArray.sort(compare);\n\n      tbody.append(...rowsArray);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    table {\n       border-collapse: collapse;\n     }\n     th, td {\n       border: 1px solid black;\n       padding: 4px;\n     }\n     th {\n       cursor: pointer;\n     }\n     th:hover {\n       background: yellow;\n     }\n  </style>\n</head>\n\n<body>\n\n  <table id=\"grid\">\n    <thead>\n      <tr>\n        <th data-type=\"number\">Umur</th>\n        <th data-type=\"string\">Nama</th>\n      </tr>\n    </thead>\n    <tbody>\n      <tr>\n        <td>5</td>\n        <td>John</td>\n      </tr>\n      <tr>\n        <td>2</td>\n        <td>Pete</td>\n      </tr>\n      <tr>\n        <td>12</td>\n        <td>Ann</td>\n      </tr>\n      <tr>\n        <td>9</td>\n        <td>Eugene</td>\n      </tr>\n      <tr>\n        <td>1</td>\n        <td>Ilya</td>\n      </tr>\n    </tbody>\n  </table>\n\n  <script>\n    // ...Kode kamu...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/3-sortable-table/task.md",
    "content": "importance: 4\n\n---\n\n# Tabel yang bisa diurutkan\n\nBuat sebuah table yang dapat diurutkan: klik pada elemen `<th>` harus mengurutkan kolom dibawahnya.\n\nSetiap `<th>` memiliki tipe pada atribut, seperti ini:\n\n```html\n<table id=\"grid\">\n  <thead>\n    <tr>\n*!*\n      <th data-type=\"number\">Umur</th>\n      <th data-type=\"string\">Nama</th>\n*/!*\n    </tr>\n  </thead>\n  <tbody>\n    <tr>\n      <td>5</td>\n      <td>John</td>\n    </tr>\n    <tr>\n      <td>10</td>\n      <td>Ann</td>\n    </tr>\n    ...\n  </tbody>\n</table>\n```\n\nPada contoh diatas kolom memiliki nomor, dan kolom kedua - string (teks). Fungsi pengurutan harus menanggani pengurutan berdasarkan tipenya.\n\nHanya tipe `\"string\"` dan `\"number\"` yang bisa di urutkan.\n\nContoh yang sudah jadi:\n\n[iframe border=1 src=\"solution\" height=190]\n\nTambahan: Tabel bisa besar, dengan banyak baris dan kolom.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* membuat badan dapat digulir, tooltip bahkan setelah digulir */\n    }\n\n    .tooltip {\n      /* beberapa style untuk tooltip, kamu bisa gunakan punya kamu*/\n      position: fixed;\n      padding: 10px 20px;\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n\n  <button data-tooltip=\"Tooltip lebih panjang dari elemen tombol\">Tombol pendek</button>\n  <button data-tooltip=\"HTML<br>tooltip\">Sebuah tombol lainnya</button>\n\n  <p>Gulir halaman untuk membuat tombol terlihat di bagian atas, periksa jika tooltip tampil dengan benar.</p>\n\n\n\n  <script>\n    let tooltipElem;\n\n    document.onmouseover = function(event) {\n      let target = event.target;\n\n      // Jika kita memiliki HTML tooltip\n      let tooltipHtml = target.dataset.tooltip;\n      if (!tooltipHtml) return;\n\n      // ...buat element tooltip\n\n      tooltipElem = document.createElement('div');\n      tooltipElem.className = 'tooltip';\n      tooltipElem.innerHTML = tooltipHtml;\n      document.body.append(tooltipElem);\n\n      // posisikan elemen tooltip diatas elemen yang beranotasi (atas-tengah)\n      let coords = target.getBoundingClientRect();\n\n      let left = coords.left + (target.offsetWidth - tooltipElem.offsetWidth) / 2;\n      if (left < 0) left = 0; // jangan lewati bagian kiri ujung jendela (window)\n\n      let top = coords.top - tooltipElem.offsetHeight - 5;\n      if (top < 0) { // jika melewati bagian ujung atas jendela (window), tampilkan dibawah ini\n        top = coords.top + target.offsetHeight + 5;\n      }\n\n      tooltipElem.style.left = left + 'px';\n      tooltipElem.style.top = top + 'px';\n    };\n\n    document.onmouseout = function(e) {\n\n      if (tooltipElem) {\n        tooltipElem.remove();\n        tooltipElem = null;\n      }\n\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* membuat badan dapat digulir, tooltip bahkan setelah digulir */\n    }\n\n    .tooltip {\n      /* beberapa style untuk tooltip, kamu bisa gunakan punya kamu*/\n      position: fixed;\n      padding: 10px 20px;\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n  <p>LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa LaLaLa</p>\n\n  <button data-tooltip=\"Tooltip lebih panjang dari elemen tombol\">Tombol pendek</button>\n  <button data-tooltip=\"HTML<br>tooltip\">Sebuah tombol lainnya</button>\n\n  <p>Gulir halaman untuk membuat tombol terlihat di bagian atas, periksa jika tooltip tampil dengan benar.</p>\n\n\n  <script>\n    // ...Kode kamu...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/4-behavior-tooltip/task.md",
    "content": "importance: 5\n\n---\n\n# Perilaku Tooltip\n\nBuat Kode JS untuk perilaku tooltip.\n\nPada saat mouse menghampir sebuah elemen dengan `data-tooltip`, tooltip harus tampil diatasnya, dan pada saat mouse itu pindah tooltipnya di sembunyikan.\n\nContoh dari HTML yang beranotasi:\n```html\n<button data-tooltip=\"Tooltip lebih panjang dari elemen tombol\">Tombol pendek</button>\n<button data-tooltip=\"HTML<br>tooltip\">Sebuah tombol lainnya</button>\n```\n\nHarus berfungsi seperti ini:\n\n[iframe src=\"solution\" height=200 border=1]\n\nPada tugas ini kita beranggapan bawah semua elemen dengan `data-tooltip` memiliki teks didalamnya, Tidak ada elemen bersarang (belum).\n\nRincian:\n\n- Jarak antara elemen dan tooltip harusnya `5px`.\n- Jika memungkinkan, tooltip harus ditengah relatif pada elemen yang beranotasi.\n- Tooltip tidak boleh melewati ujung dari jendela (_window_). Biasanya tooltip harus berada di atas elemen, tapi jika elemen itu berada pada bagian atas halaman, dan tidak ada area untuk tooltip, maka posisi tooltip dibawah elemen.\n- Konten tooltip diberikan dalam atribut 'data-tooltip'. Ini bisa menjadi HTML asalan.\n\nKamu akan membutuhkan 2 peristiwa:\n- `mouseover` akan dijalankan pada saat pointer berada di atas elemen beranotasi.\n- `mouseout` akan dijalankan pada saat pointer meninggalkan elemen yang beranotasi.\n\nTolong gunakan delegasi peristiwa: atur 2 buah penangan pada `document` untuk melacak semua \"masukan\" dan \"keluaran\" dari elemen yang memiliki `data-tooltip` dan untuk menanggani tooltip dari elemen itu.\n\nSetelah perilaku tooltip dibuat, bahkan orang yang tidak familiar dengan JavaScript bisa menambahkan elemen yang beranotasi.\n\nTambahan: Tooltip hanya bisa ditujukan satu-satu.\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/article.md",
    "content": "\n# Delegasi Peristiwa\n\nMenangkap dan pengelembungan mengizinkan kita untuk mengimplementasikan salah satu pola penanganan peristiwa paling kuat yang disebut dengan *delegasi peristiwa (_event delegation_)*.\n\nIde utama yaitu jika kita memiliki banyak elemen yang akan di tangani dengan cara yang sama, maka sebaiknya daripada memberikan sebuah penangan pada setiap elemen tersebut -- kita buat sebuah penangan (_handler_) pada elemen atas yang melingkupi semua elemen tersebut.\n\nPada penangan kita mendapatkan `event.target` untuk melihat dimanakah kejadian itu terjadi, dan akan menangani kejadian itu.\n\nMari lihat sebuah contoh -- [Ba-Gua diagram](http://en.wikipedia.org/wiki/Ba_gua) mencerminkan filosofi Cina kuno.\n\nIni dia:\n\n[iframe height=350 src=\"bagua\" edit link]\n\nHTMLnya seperti ini:\n\n```html\n<table>\n  <tr>\n    <th colspan=\"3\"><em>Bagua</em> Bagan: Arah, Elemen, Warna, Arti</th>\n  </tr>\n  <tr>\n    <td class=\"nw\"><strong>Barat Laut</strong><br>Logam<br>Perak<br>Orang Tua</td>\n    <td class=\"n\">...</td>\n    <td class=\"ne\">...</td>\n  </tr>\n  <tr>...2 buah teks seperti di atas...</tr>\n  <tr>...2 buah teks seperti di atas...</tr>\n</table>\n```\n\nTabel memiliki 9 sel, tapi bisa saja memiliki 99 atau 9999 sell, tidaklah penting.\n\n**Tugas kita adalah untuk memberikan highlight ke sel `<td>` yang di klik.**\n\nDaripada mengatur sebuah penangan `onclick` pada setiap `<td>` (yang bisa sangat banyak) -- kita akan mengatur sebuah penangan \"penangkap-semua\" pada elemen `<table>`.\n\nPenangan itu akan menggunakan `event.target` untuk mendapatkan elemen yang diklik dan menghighlightnya.\n\nKodenya:\n\n```js\nlet selectedTd;\n\n*!*\ntable.onclick = function(event) {\n  let target = event.target; // dimanakah klik terjadi?\n\n  if (target.tagName != 'TD') return; // bukan di TD? kita tidak peduli\n\n  highlight(target); // highlight elemen itu\n};\n*/!*\n\nfunction highlight(td) {\n  if (selectedTd) { // hapus elemen lain yang sudah di highlight\n    selectedTd.classList.remove('highlight');\n  }\n  selectedTd = td;\n  selectedTd.classList.add('highlight'); // menghighlight elemen yang baru\n}\n```\n\nKode seperti itu, tidak peduli berapa banyak sel yang ada pada table tersebut. Kita bisa menambahkan/menghapuskan `td` secara dinamis pada waktu kapanpun dan proses menghighlight akan tetap berfungsi.\n\nTapi, tetap ada kekurangannya.\n\nKlik mungkin tidak terjadi pada `<td>`, tapi pada elemen didalamnya.\n\nPada kasus kita jika dilihat pada HTML, kita memiliki sebuah elemen bersarang pada `<td>`, seperti `<strong>`:\n\n```html\n<td>\n*!*\n  <strong>Barat Laut</strong>\n*/!*\n  ...\n</td>\n```\n\nBiasanya, jika klik terjadi pada `<strong>` maka elemen itu akan menjadi nilai dari `event.target`.\n\n![](bagua-bubble.svg)\n\nPada penangan (_handler_) `table.onclick` kita sebaiknya mengambil `event.target` dan mencari tahu apakah klik terjadi didalam `<td>` atau tidak.\n\nIni kode yang sudah diperbaiki:\n\n```js\ntable.onclick = function(event) {\n  let td = event.target.closest('td'); // (1)\n\n  if (!td) return; // (2)\n\n  if (!table.contains(td)) return; // (3)\n\n  highlight(td); // (4)\n};\n```\n\nPenjelasan:\n1. Metode `elem.closest(selector)` akan mengembalikan elemen atas terdekat yang sama dengan pemilih (_selector_). Pada kasus kita yang dicari adalah `<td>` pada bagian atas dari elemen sumber.\n2. Jika `event.target` tidak didalam `<td>`, maka kita akan langsung mengembalikan, karena tidak ada yang bisa dilakukan.\n3. Jika pada kasus elemen bersarang didalam tabel, `event.target` bisa saja merupakan elemen `<td>`, tapi berada diluar tabel yang kita atur. Jadi kita memeriksa jika tabel itu adalah *tabel yang kita butuh* `<td>`.\n4. Dan, jika benar, maka beri highlight pada elemen itu.\n\nHasilnya, kita memiliki kode yang cepat, efisien dalam memberikan highlight, yang tidak peduli terhadap jumlah dari elemen `<td>` pada sebuah tabel.\n\n## Contoh Delegasi: tindakan dalam markup\n\nAda kegunaan lain untuk delegasi acara.\n\nBayangkan, kita mau membuat sebuah menu dengan tombol \"Simpan\", \"Muat\", \"Cari\" dan seterusnya. Dan ada sebuah objek dengan metode `simpan`, `muat`, `cari`... Bagaimana cara untuk menyamakan mereka?\n\nIde pertama yaitu dengan mengatur penangan (_handler_) berbeda pada setiap tombol, Tapi ada solusi yang lebih elegan. Kita bisa menambahkan sebuah penangan (_handler_) untuk seseluruhan menu dan menambahkan atribut `data-action` untuk tombol yang bisa memanggil/memiliki sebuah metode:\n\n```html\n<button *!*data-action=\"save\"*/!*>Klik untuk simpan</button>\n```\n\nPenangan (_handler_) membaca atribut dan mengeksekusi metode yang sama dengan atribut. Coba lihat contohnya:\n\n```html autorun height=60 run untrusted\n<div id=\"menu\">\n  <button data-action=\"save\">Simpan</button>\n  <button data-action=\"load\">Muat</button>\n  <button data-action=\"search\">Cari</button>\n</div>\n\n<script>\n  class Menu {\n    constructor(elem) {\n      this._elem = elem;\n      elem.onclick = this.onClick.bind(this); // (*)\n    }\n\n    save() {\n      alert('Menyimpan');\n    }\n\n    load() {\n      alert('Memuat');\n    }\n\n    search() {\n      alert('Mencari');\n    }\n\n    onClick(event) {\n*!*\n      let action = event.target.dataset.action;\n      if (action) {\n        this[action]();\n      }\n*/!*\n    };\n  }\n\n  new Menu(menu);\n</script>\n```\n\nHarap dicatat bahwa `this.onClick` terikat pada `this` di `(*)`. Itu penting, karena jika tidak `this` didalamnya akan menyimpan referensi ke DOM elemen (`elem`), buka ke objek `Menu`, dan `this[action]` tidak akan seperti yang kita inginkan.\n\nJadi,apakah keuntuk yang diberikan delegasi kepada kita disini?\n\n```compare\n+ Kita teidak perlu lagi menulis kode untuk mengatur penangan (_handler_) untuk setiap tombol. Kita hanya perlu membuat sebuah metode dan menaruh markup didalamnya.\n+ Struktur HTML menjadi fleksible, dan kita bisa menambah/menghapus tombol kapanpun kita mau.\n```\n\nKita juga bisa menggunakan _class_ `.action-save`, `action-load`, tapi sebuah atribut `data-action` lebih baik secara semantik. Dan kita bisa gunakan itu pada aturan CSS juga.  \n\n## Perilaku pola\n\nKita juga bisa menggunakan delegasi peristiwa untuk menambahkan 'perilaku' kepada elemen secara deklarasi, dengan atribut khusus dan _class_.\n\nPola memiliki 2 bagian:\n1. Kita tambahkan sebuah atribut khusus ke sebuah elemen yang menjelaskan perilakunya.\n2. Penangan dokumen secara umum untuk melacak peristiwa, dan jika sebuah peristiwa terjadi pada elemen yang memiliki atribut khusus -- jalankan sebuah proses.\n\n### Perilaku: Menghitung\n\nContohnya, disini atribut `data-counter` menambahkan sebuah perilaku: \"Menambah nilai pada klik\" ke tombol:\n\n```html run autorun height=60\nPenghitung: <input type=\"button\" value=\"1\" data-counter>\nPenghitung lainnya: <input type=\"button\" value=\"2\" data-counter>\n\n<script>\n  document.addEventListener('click', function(event) {\n\n    if (event.target.dataset.counter != undefined) { // Jika ada atributnya...\n      event.target.value++;\n    }\n\n  });\n</script>\n```\n\nJika kita mengklik sebuah tombol -- nilainya akan bertambah. Bukan tombol, tapi pendekatan secara umum penting pada kasus ini.\n\nBisa ada banyak atribut dengan `data-counter` sebanyak yang kita mau. Kta bisa menambah atribut baru ke HTML kapanpun kita mau. Menggunakan delegasi peristiwa kita \"memperpanjang\" HTML, menambahkan sebuah atribut baru untuk menjelaskan sebuah perilaku baru.\n\n```warn header=\"Untuk penangan tingkat dokumen -- selalu gunakan `addEventListener`\"\nPada saat kita mengatur sebuah penangan peristiwa (_event handler_) ke objek `dokumen`, sebaiknya selalu gunakan `addEvenListener`, dan bukan `document.on<event>`, karena yang kedua akan mengakibatkan konflik: penangan baru akan menimpah penangan yang lama.\n\nUntuk projek asli, adalah normal untuk memiliki banyak penangan (_handler_) yang di atur ke `document` pada bagian code yang berbeda.\n```\n\n### Perilaku: Pengalih\n\nSatu lagi contoh dari perilaku. Sebuah klik pada elemen dengan atribut `data-toggle-id` akan menampilkan/menyembunyikan elemen dengan `id` yang sama:\n\n```html autorun run height=60\n<button *!*data-toggle-id=\"subscribe-mail\"*/!*>\n  Tampilkan formulir berlangganan\n</button>\n\n<form id=\"subscribe-mail\" hidden>\n  Email kamu: <input type=\"email\">\n</form>\n\n<script>\n*!*\n  document.addEventListener('click', function(event) {\n    let id = event.target.dataset.toggleId;\n    if (!id) return;\n\n    let elem = document.getElementById(id);\n\n    elem.hidden = !elem.hidden;\n  });\n*/!*\n</script>\n```\n\nCatat lagi apa yang kita lakukan. Sekarang, untuk menambahkan fungsi beralih pada elemen -- tidak memerlukan pengetahuan tentang JavaScript, hanya perlu menggunakan atribut `data-toggle-id`.\n\nHal ini akan sangat menyederhanakan proses -- tidak perlu menulis JavaScript untuk setiap elemen. Hanya perlu menggunakan perilaku. Penangan (_handler_) tingkat dokumen akan membuat proses ini berfungsi pada setiap elemen yang ada di dalam halaman tersebut.\n\nKita juga bisa menggabungkan beberapa perilaku pada sebuah elemen.\n\n\"Perilaku\" pola bisa menjadi alternatif terhadap fargmen kecil JavaScript.\n\n## Ringkisan\n\nDelegasi peristiwa sangatlah keren! Itu salah satu pola yang paling berguna untuk peristiwa DOM.\n\nItu sering digunakan untuk menambahkan penangan untuk elemen yang mirip, tapi bukan hanya untuk itu.\n\nAlgoritmanya:\n\n1. Taruh sebuah penangan (_handler_) pada elemen atas.\n2. Di penangan (_handler_) -- periksa sumber elemen dengan menggunakan `event.target`.\n3. Jika peristiwa terjadi didalam elemen yang kita inginkan, maka tangani peristiwa itu.\n\nKeuntungan:\n\n```compare\n+ Menyederhanakan proses inisialisasi dan menghemat memori: tidak perlu membuat banyak penangan (_handler_).\n+ Sedikit Kode: saat menambahkan dan menghapus elemen, kita tidak perlu tambah/hapus penangan (_handler_).\n+ Modifikasi DOM: Kita bisa secara banyak menambahkan/menghapuskan elemen dengan `innerHTML` dan sejenisnya.\n```\n\nDelegasi tentu juga memiliki batasannya:\n\n```compare\n- Pertama, peristiwa harus bisa mengelembung. Bebebrapa peristiwa tidak mengelembung. Juga, penangan (_handler_) pada level bawah tidak boleh menggunakan `event.stopPropagation()`.\n- Kedua, delegasi mungkin menambahkan muatan pada CPU, karena penangan (_handler_) pada level atas akan bereaksi pada peristiwa yang terjadi didalam elemen itu, tidak peduli jika peristiwa itu yang kita inginkan atau tidak. Tapi biasanya proses muatannya tidak besar dan bisa diabaikan, jadi kita tidak perlu memperhitungkannya.\n```\n"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/bagua.view/bagua.css",
    "content": "#bagua-table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#bagua-table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n}\n\n#bagua-table .nw {\n  background: #999;\n}\n\n#bagua-table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#bagua-table .ne {\n  background: #ff6;\n}\n\n#bagua-table .w {\n  background: #ff0;\n}\n\n#bagua-table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#bagua-table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#bagua-table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#bagua-table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#bagua-table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#bagua-table .highlight {\n  background: red;\n}"
  },
  {
    "path": "2-ui/2-events/03-event-delegation/bagua.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"bagua.css\">\n\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Bagan: Arah, Elemen, Warna, Arti</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Barat Laut</strong>\n        <br>Logam\n        <br>Perak\n        <br>Orang Tua\n      </td>\n      <td class=\"n\"><strong>Utara</strong>\n        <br>Air\n        <br>Biru\n        <br>Mengubah\n      </td>\n      <td class=\"ne\"><strong>Timur Laut</strong>\n        <br>Tanah\n        <br>Kuning\n        <br>Arah\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>Barat</strong>\n        <br>Logam\n        <br>Emas\n        <br>Muda\n      </td>\n      <td class=\"c\"><strong>Tengah</strong>\n        <br>Semua\n        <br>Ungu\n        <br>Harmonis\n      </td>\n      <td class=\"e\"><strong>Timur</strong>\n        <br>Kayu\n        <br>Biru\n        <br>Masa Depan\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Barat Daya</strong>\n        <br>Tanah\n        <br>Coklat\n        <br>Ketenangan\n      </td>\n      <td class=\"s\"><strong>Selatan</strong>\n        <br>Api\n        <br>Oranye\n        <br>Ketenaran\n      </td>\n      <td class=\"se\"><strong>Tenggara</strong>\n        <br>Kayu\n        <br>Hijau\n        <br>Romansa\n      </td>\n    </tr>\n\n  </table>\n\n  <script>\n    let table = document.getElementById('bagua-table');\n\n    let selectedTd;\n\n    table.onclick = function(event) {\n      let target = event.target;\n\n      while (target != this) {\n        if (target.tagName == 'TD') {\n          highlight(target);\n          return;\n        }\n        target = target.parentNode;\n      }\n    }\n\n    function highlight(node) {\n      if (selectedTd) {\n        selectedTd.classList.remove('highlight');\n      }\n      selectedTd = node;\n      selectedTd.classList.add('highlight');\n    }\n  </script>\n\n</body>\n\n</html>"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/1-why-return-false-fails/solution.md",
    "content": "Pada saat _browser_ membaca atribut `on*` seperti `onclick`, _browser_ akan membuat sebuah penangan (_handler_) dari kontennya.\n\nUntuk `onclick\"handler()\"` fungsinya akan menjadi:\n\n```js\nfunction(event) {\n  handler() // konten dari onclick\n}\n```\n\nSekarang kita bisa melihat bahwa nilai yang dikembalikan oleh `handler()` tidak digunakan dan tidak mempengaruhi hasilnya.\n\nCara memperbaikinnya mudah:\n\n```html run\n<script>\n  function handler() {\n    alert(\"...\");\n    return false;\n  }\n</script>\n\n<a href=\"https://w3.org\" onclick=\"*!*return handler()*/!*\">w3.org</a>\n```\n\nKita juga bisa menggunakan `event.preventDefault()`, seperti ini:\n\n```html run\n<script>\n*!*\n  function handler(event) {\n    alert(\"...\");\n    event.preventDefault();\n  }\n*/!*\n</script>\n\n<a href=\"https://w3.org\" onclick=\"*!*handler(event)*/!*\">w3.org</a>\n```\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/1-why-return-false-fails/task.md",
    "content": "importance: 3\n\n---\n\n# Mengapa \"return false\" tidak berfungsi?\n\nKenapa pada kode dibawah `return false` tidak berfungsi sama sekali?\n\n```html autorun run\n<script>\n  function handler() {\n    alert( \"...\" );\n    return false;\n  }\n</script>\n\n<a href=\"https://w3.org\" onclick=\"handler()\">browser akan membuka w3.org</a>\n```\n\n_browser_ akan mengikuti URL yang di klik, tapi kita tidak mau hal itu terjadi.\n\nBagaimana cara memperbaikinya?\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.md",
    "content": "Itu merupakan salah satu cara yang bagus dalam pemanfaatan pola delegasi peristiwa.\n\nPada kehidupan nyata kita bisa mengirim permintaan \"logging\" ke server yang menyimpan informasi tentang dari mana pengujung meninggalkan website daripada menanyakannya secara langsung. Atau kita bisa memuat konten dan menunjukannya tepat pada halaman (jika diizinkan).\n\nYang kita butuhkan hanyalah menangkap `contents.onclick` dan menggunakan `confim` untuk menanyakan pengguna. Sebuah ide bagus adalah dengan menggunakan `link.getAttribute('href')` dari pada menggunakan `link.href` untuk URL. Lihat solusinya untuk rincian.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #contents {\n      padding: 5px;\n      border: 1px green solid;\n    }\n  </style>\n</head>\n\n<body>\n\n  <fieldset id=\"contents\">\n    <legend>#kontent</legend>\n    <p>\n      Bagaimana jika membaca di <a href=\"https://wikipedia.org\">Wikipedia</a> atau kunjungi <a href=\"https://w3.org\"><i>W3.org</i></a> dan belajar tentang standar terbaru?\n    </p>\n  </fieldset>\n\n  <script>\n    contents.onclick = function(event) {\n\n      function handleLink(href) {\n        let isLeaving = confirm(`Pergi ke ${href}?`);\n        if (!isLeaving) return false;\n      }\n\n      let target = event.target.closest('a');\n\n      if (target && contents.contains(target)) {\n        return handleLink(target.getAttribute('href'));\n      }\n    };\n  </script>\n  \n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #contents {\n      padding: 5px;\n      border: 1px green solid;\n    }\n  </style>\n</head>\n\n<body>\n\n  <fieldset id=\"contents\">\n    <legend>#kontent</legend>\n    <p>\n      Bagaimana jika membaca di <a href=\"https://wikipedia.org\">Wikipedia</a> atau kunjungi <a href=\"https://w3.org\"><i>W3.org</i></a> dan belajar tentang standar terbaru?\n    </p>\n  </fieldset>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/2-catch-link-navigation/task.md",
    "content": "importance: 5\n\n---\n# Tangkap link pada elemen\n\nBuat semua link yang ada didalam elemen dengan `id=\"contents\"` akan menanyakan kepada pengguna jika mereka mau meninggalkan website. Dan jika mereka tidak mau maka halaman tidak akan berpindah.\n\nSeperti ini:\n\n[iframe height=100 border=1 src=\"solution\"]\n\nRincian:\n\n- HTML didalam elemen bisa di muat dan di buat kembali secara dinamis secara acak, jadi kita tidak bisa menemukan semua link dan memberikan penangan (_handler_). Gunakan delegasi peristiwa.\n- Kontent bisa saja merupakan tag bersarang. Di dalam link juga, seperti `<a href=\"..\"><i>...</i></a>`.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.md",
    "content": "The solution is to assign the handler to the container and track clicks. If a click is on the `<a>` link, then change `src` of `#largeImg` to the `href` of the thumbnail.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/gallery.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font: 75%/120% sans-serif;\n}\n\nh2 {\n  font: bold 190%/100% sans-serif;\n  margin: 0 0 .2em;\n}\n\nh2 em {\n  font: normal 80%/100% sans-serif;\n  color: #999999;\n}\n\n#largeImg {\n  border: solid 1px #ccc;\n  width: 550px;\n  height: 400px;\n  padding: 5px;\n}\n\n#thumbs a {\n  border: solid 1px #ccc;\n  width: 100px;\n  height: 100px;\n  padding: 3px;\n  margin: 2px;\n  float: left;\n}\n\n#thumbs a:hover {\n  border-color: #FF9900;\n}\n\n#thumbs li {\n  list-style: none;\n}\n\n#thumbs {\n  margin: 0;\n  padding: 0;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <title>Gallery</title>\n  <link rel=\"stylesheet\" href=\"gallery.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p><img id=\"largeImg\" src=\"https://en.js.cx/gallery/img1-lg.jpg\" alt=\"Large image\"></p>\n\n  <ul id=\"thumbs\">\n    <!-- the browser shows a small built-in tooltip on hover with the text from \"title\" attribute -->\n    <li>\n      <a href=\"https://en.js.cx/gallery/img2-lg.jpg\" title=\"Image 2\"><img src=\"https://en.js.cx/gallery/img2-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img3-lg.jpg\" title=\"Image 3\"><img src=\"https://en.js.cx/gallery/img3-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img4-lg.jpg\" title=\"Image 4\"><img src=\"https://en.js.cx/gallery/img4-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img5-lg.jpg\" title=\"Image 5\"><img src=\"https://en.js.cx/gallery/img5-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img6-lg.jpg\" title=\"Image 6\"><img src=\"https://en.js.cx/gallery/img6-thumb.jpg\"></a>\n    </li>\n  </ul>\n\n  <script>\n    thumbs.onclick = function(event) {\n      let thumbnail = event.target.closest('a');\n\n      if (!thumbnail) return;\n      showThumbnail(thumbnail.href, thumbnail.title);\n      event.preventDefault();\n    }\n\n    function showThumbnail(href, title) {\n      largeImg.src = href;\n      largeImg.alt = title;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/gallery.css",
    "content": "body {\n  margin: 0;\n  padding: 0;\n  font: 75%/120% sans-serif;\n}\n\nh2 {\n  font: bold 190%/100% sans-serif;\n  margin: 0 0 .2em;\n}\n\nh2 em {\n  font: normal 80%/100% sans-serif;\n  color: #999999;\n}\n\n#largeImg {\n  border: solid 1px #ccc;\n  width: 550px;\n  height: 400px;\n  padding: 5px;\n}\n\n#thumbs a {\n  border: solid 1px #ccc;\n  width: 100px;\n  height: 100px;\n  padding: 3px;\n  margin: 2px;\n  float: left;\n}\n\n#thumbs a:hover {\n  border-color: #FF9900;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <title>Gallery</title>\n  <link rel=\"stylesheet\" href=\"gallery.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p><img id=\"largeImg\" src=\"https://en.js.cx/gallery/img1-lg.jpg\" alt=\"Large image\"></p>\n\n  <ul id=\"thumbs\">\n    <!-- the browser shows a small built-in tooltip on hover with the text from \"title\" attribute -->\n    <li>\n      <a href=\"https://en.js.cx/gallery/img2-lg.jpg\" title=\"Image 2\"><img src=\"https://en.js.cx/gallery/img2-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img3-lg.jpg\" title=\"Image 3\"><img src=\"https://en.js.cx/gallery/img3-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img4-lg.jpg\" title=\"Image 4\"><img src=\"https://en.js.cx/gallery/img4-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img5-lg.jpg\" title=\"Image 5\"><img src=\"https://en.js.cx/gallery/img5-thumb.jpg\"></a>\n    </li>\n    <li>\n      <a href=\"https://en.js.cx/gallery/img6-lg.jpg\" title=\"Image 6\"><img src=\"https://en.js.cx/gallery/img6-thumb.jpg\"></a>\n    </li>\n  </ul>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/3-image-gallery/task.md",
    "content": "importance: 5\n\n---\n\n# Image gallery\n\nCreate an image gallery where the main image changes by the click on a thumbnail.\n\nLike this:\n\n[iframe src=\"solution\" height=600]\n\nP.S. Use event delegation.\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/article.md",
    "content": "# _Browser default actions_\n\nBanyak peristiwa yang otomatis menjalankan sebuah aksi oleh _browser_.\n\nContohnya:\n\n- Sebuah klik pada link - akan mengarahkan ke URL tersebut.\n- Sebuah klik pada tombol formulir - akan melakukan proses pengiriman data formulir ke _server_.\n- Menekan tombol _mouse_ pada sebuah teks dan mengerakannya - akan memilih teks tersebut. \n\nJika ke menanggani sebuah peristiwa di JavaScript, kita mungkin tidak mau aksi bawaan dari _browser_ terjadi, dan kita mau membuat sebuah aksi baru.\n\n## Mencegah aksi _browser_\n\nAda 2 cara untuk memberitahukan _browser_ bahwa kita tidak mau peristiwa itu terjadi:\n\n- Cara utama dengan menggunakan objek `event`. Ada sebuah metode dengan nama `event.preventDefault()`.\n- Jika penangan (_handler_) di atur menggunakan `on<event>` (bukan `addEventListener`), maka mengembalikan `false` juga bisa befungsi.\n\nPada HTML dibawah, sebuah klik pada link tidak akan memindahkan halaman, _browser_ tidak melakukan apapun:\n\n```html autorun height=60 no-beautify\n<a href=\"/\" onclick=\"return false\">Klik disini</a>\natau\n<a href=\"/\" onclick=\"event.preventDefault()\">disini</a>\n```\n\nPada contoh selanjutnya kita akan menggunakan teknik untuk menu yang dibuat oleh JavaScript.\n\n```warn header=\"Returning `false` dari sebuah penangan adalah sebuah pengecualian\"\nNilai yang di kembalikan oleh penangan (_handler_) bisanya dibiar.\n\nHanya pada `return false` terdapat pengecualian jika tejadi pada sebuah penangan (_handler_) yang diatur menggunakan `on<event>`.\n\nPada semua kasus yang lain, nilai `return` akan dibiarkan. Khusunya, tidak masuk akal untuk mengembalikan `true`.\n```\n\n### Contoh: menu\n\nBayangkan menu situs, seperti ini:\n\n```html\n<ul id=\"menu\" class=\"menu\">\n  <li><a href=\"/html\">HTML</a></li>\n  <li><a href=\"/javascript\">JavaScript</a></li>\n  <li><a href=\"/css\">CSS</a></li>\n</ul>\n```\n\nDan begini tampilannya jika ditambahkan CSS:\n\n[iframe height=70 src=\"menu\" link edit]\n\nItem menu diimplementasikan sebagai HTML-link `<a>`, bukan tombol `<button>`. Ada beberapa alasan untuk melakukannya, misalnya: \n\n- Banyak orang suka menggunakan \"klik kanan\" -- \"buka di jendela baru\". Jika kita menggunakan `<button>` atau `<span>`, itu tidak akan berhasil.\n- Mesin pencari mengikuti tautan `<a href=\"...\">` saat mengindeks.\n\nJadi kami menggunakan `<a>` di markup. Tetapi biasanya kami bermaksud menangani klik dalam JavaScript. Jadi kita harus mencegah tindakan bawaan _browser_.\n\nSeperti ini:\n\n```js\nmenu.onclick = function(event) {\n  if (event.target.nodeName != 'A') return;\n\n  let href = event.target.getAttribute('href');\n  alert( href ); // ...bisa memuat dari server, membuat UI dll\n\n*!*\n  return false; // mencegah bawaan browser (tidak pergi ke URL)\n*/!*\n};\n```\n\nJika kita menghilangkan `return false`, maka setelah kode kita dijalankan, browser akan melakukan \"tindakan bawaan\" -- menavigasi ke URL di `href`. Dan kami tidak membutuhkannya di sini, karena kami menangani klik sendiri.\n\nDan juga, menggunakan delegasi peristiwa di sini membuat menu kami sangat fleksibel. Kita dapat menambahkan daftar bersarang dan menatanya menggunakan CSS ke \"slide down\".\n\n````smart header=\"Peristiwa lanjutan\"\nPeristiwa tertentu mengalir satu ke yang lain. Jika kita mencegah peristiwa pertama, tidak akan ada yang kedua.\n\nContohnya, `mousedown` pada sebuah `<input>` mengarah ke fokus di dalamnya, dan peristiwa `focus`. Jika kita mencegah peristiwa `mousedown`, tidak ada fokus.\n\nCoba klik pada `<input>`dibawah -- peristiwa `focus` terjadi. Tapi jika kita klik pada yang kedua, tidak ada fokus.\n\n```html run autorun\n<input value=\"Ada fokus\" onfocus=\"this.value=''\">\n<input *!*onmousedown=\"return false\"*/!* onfocus=\"this.value=''\" value=\"Klik saya\">\n```\n\nItu karena tindakan browser dibatalkan pada `mousedown`. Pemfokusan masih dimungkinkan jika kita menggunakan cara lain untuk memasukkan input. Misalnya, tombol `key:Tab` untuk beralih dari input pertama ke input kedua. Tapi tidak dengan klik mouse lagi.\n````\n\n## Pilihan penangan \"passive\"\n\nPilihan `passive: true` opsional dari `addEventListener` memberi sinyal kepada _browser_ bahwa penangan tidak akan memanggil `preventDefault()`.\n\nMengapa itu mungkin diperlukan?\n\nAda beberapa peristiwa seperti `touchmove` pada perangkat seluler (ketika pengguna menggerakkan jari mereka melintasi layar), yang menyebabkan pengguliran secara bawaan, tetapi pengguliran tersebut dapat dicegah menggunakan `preventDefault()` di pengendali.\n\nJadi, ketika _browser_ mendeteksi peristiwa seperti itu, _browser_ harus terlebih dahulu memproses semua penangan (_handler_), dan kemudian jika `preventDefault` tidak dipanggil di mana pun, _browser_ dapat melanjutkan dengan menggulir. Itu dapat menyebabkan penundaan dan \"kegelisahan\" yang tidak perlu di UI.\n\nPilihan `passive: true` memberi tahu _browser_ bahwa penangan (_handler_) tidak akan membatalkan pengguliran. Kemudian _browser_ segera menggulir memberikan pengalaman lancar maksimal, dan peristiwa ditangani dengan cara.\n\nUntuk beberapa _browser_ (Firefox, Chrome), `passive` atur menjadi `true` secara bawaan untuk peristiwa `touchstart` dan `touchmove`.\n\n\n## event.defaultPrevented\n\nProperti `event.defaultPrevented` adalah `true` jika tindakan default dicegah, dan `false` jika sebaliknya.\n\nAda kasus penggunaan yang menarik untuk itu. \n\nAnda ingat di bab <info:bubbling-and-capturing> kita berbicara tentang `event.stopPropagation()` dan mengapa menghentikan pengelembungan itu buruk?\n\nTerkadang kita dapat menggunakan `event.defaultPrevented` sebagai gantinya, untuk memberi sinyal pada penangan peristiwa lain bahwa peristiwa tersebut ditangani.\n\nMari kita lihat contoh praktisnya.\n\nSecara bawaan, _browser_ pada peristiwa `contextmenu` (klik kanan mouse) menampilkan menu konteks dengan pilihan standar. Kita bisa mencegahnya dan menunjukkannya sendiri, seperti ini:\n\n```html autorun height=50 no-beautify run\n<button>Klik kanan menampilkan menu konteks browser</button>\n\n<button *!*oncontextmenu=\"alert('Buat menu baru'); return false\"*/!*>\n  Klik kanan menampilkan menu konteks browser\n</button>\n```\n\nSekarang, selain menu konteks itu, kami ingin menerapkan menu konteks seluruh dokumen.\n\nSetelah klik kanan, menu konteks terdekat akan muncul.\n\n```html autorun height=80 no-beautify run\n<p>Klik kanan menampilkan menu konteks browser</p>\n<button id=\"elem\">Klik kanan menampilkan menu konteks browser</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Tombol konteks menu\");\n  };\n\n  document.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Dokumen konteks menu\");\n  };\n</script>\n```\n\nMasalahnya adalah ketika kita mengklik `elem`, kita mendapatkan dua menu: level tombol dan (peristiwa muncul) menu level dokumen.\n\nBagaimana memperbaikinya? Salah satu solusinya adalah dengan berpikir seperti: \"Ketika kita menangani klik kanan pada pengendali tombol, mari hentikan gelembungnya\" dan gunakan `event.stopPropagation()`:\n\n```html autorun height=80 no-beautify run\n<p>Klik kanan menampilkan menu konteks browser</p>\n<button id=\"elem\">Klik kanan menampilkan menu konteks browser (diperbaiki dengan event.stopPropagation)</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n*!*\n    event.stopPropagation();\n*/!*\n    alert(\"Tombol konteks menu\");\n  };\n\n  document.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Dokumen konteks menu\");\n  };\n</script>\n```\n\nSekarang menu tingkat tombol berfungsi sebagaimana dimaksud. Tapi bayarannya tinggi. Kami selamanya menolak akses ke informasi dari klik kanan untuk kode yang lain, termasuk penghitung yang mengumpulkan statistik dan sebagainya. Hal ini sangat tidak bijaksana.\n\nSolusi alternatif adalah dengan memeriksa penangan (_handler_) `document` jika tindakan bawaan dicegah? Jika demikian, maka peristiwa itu ditangani, dan kita tidak perlu bereaksi.\n\n\n```html autorun height=80 no-beautify run\n<p>Klik kanan menampilkan menu konteks browser (Menambah pemeriksa untuk event.defaultPrevented)</p>\n<button id=\"elem\">Klik kanan menampilkan menu konteks browser</button>\n\n<script>\n  elem.oncontextmenu = function(event) {\n    event.preventDefault();\n    alert(\"Tombol konteks menu\");\n  };\n\n  document.oncontextmenu = function(event) {\n*!*\n    if (event.defaultPrevented) return;\n*/!*\n\n    event.preventDefault();\n    alert(\"Dokumen konteks menu\");\n  };\n</script>\n```\n\nSekarang semuanya berfungsi dengan benar. Jika kita memiliki elemen bersarang, dan masing-masing elemen memiliki menu konteksnya sendiri, itu juga akan berfungsi. Pastikan untuk memeriksa `event.defaultPrevented` di setiap penangan (_handler_) `contextmenu`.\n\n```smart header=\"event.stopPropagation() dan event.preventDefault()\"\nSeperti yang bisa kita lihat dengan jelas, `event.stopPropagation()` dan `event.preventDefault()` (juga dikenal dengan `return false`) adalah dua hal yang berbeda. Dan mereka tidak terkait satu dengan yang lain.\n```\n\n```smart header=\"Arsitektur menu konteks bersarang\"\nAda juga cara alternatif untuk mengimplementasikan menu konteks bersarang. Salah satunya adalah memiliki satu objek global dengan penangan (_handler_) untuk `document.oncontextmenu`, dan juga metode yang memungkinkan kita untuk menyimpan penangan (_handler_) lain di dalamnya.\n\nObjek akan menangkap klik kanan apa pun, melihat melalui penangan (_handler_) yang tersimpan dan menjalankan yang sesuai.\n\nTapi kemudian setiap bagian kode yang menginginkan menu konteks harus tahu tentang objek itu dan menggunakan bantuannya alih-alih penangan (_handler_) `contextmenu` sendiri.\n```\n\n## Rincian\n\nAda banyak aksi bawaan _browser_:\n\n- `mousedown` -- memulai pemilihan (gerakkan mouse untuk memilih).\n- `click` pada `<input type=\"checkbox\">` -- centang/hapus centang pada `input`.\n- `submit` -- mengklik `<input type=\"submit\">` atau menekan `key:Enter` di dalam bidang formulir menyebabkan peristiwa ini terjadi, dan browser mengirimkan formulir setelahnya.\n- `keydown` -- menekan tombol dapat menyebabkan penambahan karakter ke dalam bidang, atau tindakan lainnya.\n- `contextmenu` -- peristiwa terjadi dengan klik kanan, tindakannya adalah menampilkan menu konteks browser.\n- ...ada lagi...\n\nSemua tindakan bawaan dapat dicegah jika kita ingin menangani peristiwa (_event_) secara eksklusif dengan JavaScript.\n\nUntuk mencegah peristiwa (_event_) bawaan -- gunakan `event.preventDefault()` atau `return false`. Metode kedua hanya berfungsi untuk penangan (_handler_) yang ditetapkan dengan `on<event>`.\n\nPilihan `passive: true` dari `addEventListener` memberi tahu _browser_ bahwa tindakan tersebut tidak akan dicegah. Itu berguna untuk beberapa aktivitas seluler, seperti `touchstart` dan `touchmove`, untuk memberi tahu _browser_ bahwa _browser_ tidak boleh menunggu semua penangan (_handler_) selesai sebelum menggulir.\n\nJika tindakan bawaan dicegah, nilai `event.defaultPrevented` menjadi `true`, jika tidak maka `false`.\n\n```warn header=\"Tetap semantik, jangan menyalahgunakan elemen\"\nSecara teknis, dengan mencegah tindakan bawaan dan menambahkan JavaScript, kita dapat menyesuaikan perilaku elemen apa pun. Misalnya, kita dapat membuat tautan `<a>` berfungsi seperti tombol, dan tombol `<button>` berfungsi sebagai tautan (mengalihkan ke URL lain atau lebih).\n\nTapi secara umum kita harus menjaga makna semantik dari elemen HTML. Misalnya, `<a>` harus melakukan navigasi, bukan tombol.\n\nSelain menjadi \"hal yang baik\", itu membuat HTML Anda lebih baik dalam hal aksesibilitas.\n\nJuga jika kita mempertimbangkan contoh dengan `<a>`, maka harap dicatat: _browser_ memungkinkan kita untuk membuka link tersebut di jendela baru (dengan mengklik kanan link tersebut dan cara lain). Dan penggunan menyukai hal tersebut. Tetapi jika kita membuat tombol berperilaku sebagai tautan dengan menggunakan JavaScript dan bahkan terlihat seperti tautan menggunakan CSS, maka fitur browser khusus `<a>` tetap tidak akan berfungsi pada tombol itu.\n```\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"menu.css\" />\n</head>\n\n<body>\n\n  <ul id=\"menu\" class=\"menu\">\n    <li><a href=\"/html\">HTML</a></li>\n    <li><a href=\"/javascript\">JavaScript</a></li>\n    <li><a href=\"/css\">CSS</a></li>\n  </ul>\n\n  <script src=\"menu.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/menu.css",
    "content": ".menu li {\n  display: inline-block;\n  margin: 0;\n}\n\n.menu > li a {\n  display: inline-block;\n  margin: 0 2px;\n  outline: none;\n  text-align: center;\n  text-decoration: none;\n  font: 14px/100% sans-serif;\n  padding: .5em 2em .55em;\n  text-shadow: 0 1px 1px rgba(0, 0, 0, .3);\n  border-radius: .5em;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, .2);\n  color: #d9eef7;\n  border: solid 1px #0076a3;\n  background: #0095cd;\n}\n\n.menu > li:hover a {\n  text-decoration: none;\n  background: #007ead;\n}"
  },
  {
    "path": "2-ui/2-events/04-default-browser-action/menu.view/menu.js",
    "content": "menu.onclick = function(event) {\n  if (event.target.nodeName != 'A') return;\n\n  let href = event.target.getAttribute('href');\n  alert(href);\n\n  return false; // mencegah perubahan URL\n}\n"
  },
  {
    "path": "2-ui/2-events/05-dispatch-events/article.md",
    "content": "# Dispatching custom events\n\nWe can not only assign handlers, but also generate events from JavaScript.\n\nCustom events can be used to create \"graphical components\". For instance, a root element of our own JS-based menu may trigger events telling what happens with the menu: `open` (menu open), `select` (an item is selected) and so on. Another code may listen for the events and observe what's happening with the menu.\n\nWe can generate not only completely new events, that we invent for our own purposes, but also built-in ones, such as `click`, `mousedown` etc. That may be helpful for automated testing.\n\n## Event constructor\n\nBuilt-in event classes form a hierarchy, similar to DOM element classes. The root is the built-in [Event](http://www.w3.org/TR/dom/#event) class.\n\nWe can create `Event` objects like this:\n\n```js\nlet event = new Event(type[, options]);\n```\n\nArguments:\n\n- *type* -- event type, a string like `\"click\"` or our own like `\"my-event\"`.\n- *options* -- the object with two optional properties:\n  - `bubbles: true/false` -- if `true`, then the event bubbles.\n  - `cancelable: true/false` -- if `true`, then the \"default action\"  may be prevented. Later we'll see what it means for custom events.\n\n  By default both are false: `{bubbles: false, cancelable: false}`.\n\n## dispatchEvent\n\nAfter an event object is created, we should \"run\" it on an element using the call `elem.dispatchEvent(event)`.\n\nThen handlers react on it as if it were a regular browser event. If the event was created with the `bubbles` flag, then it bubbles.\n\nIn the example below the `click` event is initiated in JavaScript. The handler works same way as if the button was clicked:\n\n```html run no-beautify\n<button id=\"elem\" onclick=\"alert('Click!');\">Autoclick</button>\n\n<script>\n  let event = new Event(\"click\");\n  elem.dispatchEvent(event);\n</script>\n```\n\n```smart header=\"event.isTrusted\"\nThere is a way to tell a \"real\" user event from a script-generated one.\n\nThe property `event.isTrusted` is `true` for events that come from real user actions and `false` for script-generated events.\n```\n\n## Bubbling example\n\nWe can create a bubbling event with the name `\"hello\"` and catch it on `document`.\n\nAll we need is to set `bubbles` to `true`:\n\n```html run no-beautify\n<h1 id=\"elem\">Hello from the script!</h1>\n\n<script>\n  // catch on document...\n  document.addEventListener(\"hello\", function(event) { // (1)\n    alert(\"Hello from \" + event.target.tagName); // Hello from H1\n  });\n\n  // ...dispatch on elem!\n  let event = new Event(\"hello\", {bubbles: true}); // (2)\n  elem.dispatchEvent(event);\n\n  // the handler on document will activate and display the message.\n\n</script>\n```\n\n\nNotes:\n\n1. We should use `addEventListener` for our custom events, because `on<event>` only exists for built-in events, `document.onhello` doesn't work.\n2. Must set `bubbles:true`, otherwise the event won't bubble up.\n\nThe bubbling mechanics is the same for built-in (`click`) and custom (`hello`) events. There are also capturing and bubbling stages.\n\n## MouseEvent, KeyboardEvent and others\n\nHere's a short list of classes for UI Events from the [UI Event specification](https://www.w3.org/TR/uievents):\n\n- `UIEvent`\n- `FocusEvent`\n- `MouseEvent`\n- `WheelEvent`\n- `KeyboardEvent`\n- ...\n\nWe should use them instead of `new Event` if we want to create such events. For instance, `new MouseEvent(\"click\")`.\n\nThe right constructor allows to specify standard properties for that type of event.\n\nLike `clientX/clientY` for a mouse event:\n\n```js run\nlet event = new MouseEvent(\"click\", {\n  bubbles: true,\n  cancelable: true,\n  clientX: 100,\n  clientY: 100\n});\n\n*!*\nalert(event.clientX); // 100\n*/!*\n```\n\nPlease note: the generic `Event` constructor does not allow that.\n\nLet's try:\n\n```js run\nlet event = new Event(\"click\", {\n  bubbles: true, // only bubbles and cancelable\n  cancelable: true, // work in the Event constructor\n  clientX: 100,\n  clientY: 100\n});\n\n*!*\nalert(event.clientX); // undefined, the unknown property is ignored!\n*/!*\n```\n\nTechnically, we can work around that by assigning directly `event.clientX=100` after creation. So that's a matter of convenience and following the rules. Browser-generated events always have the right type.\n\nThe full list of properties for different UI events is in the specification, for instance, [MouseEvent](https://www.w3.org/TR/uievents/#mouseevent).\n\n## Custom events\n\nFor our own, completely new events types like `\"hello\"` we should use `new CustomEvent`. Technically [CustomEvent](https://dom.spec.whatwg.org/#customevent) is the same as `Event`, with one exception.\n\nIn the second argument (object) we can add an additional property `detail` for any custom information that we want to pass with the event.\n\nFor instance:\n\n```html run refresh\n<h1 id=\"elem\">Hello for John!</h1>\n\n<script>\n  // additional details come with the event to the handler\n  elem.addEventListener(\"hello\", function(event) {\n    alert(*!*event.detail.name*/!*);\n  });\n\n  elem.dispatchEvent(new CustomEvent(\"hello\", {\n*!*\n    detail: { name: \"John\" }\n*/!*\n  }));\n</script>\n```\n\nThe `detail` property can have any data. Technically we could live without, because we can assign any properties into a regular `new Event` object after its creation. But `CustomEvent` provides the special `detail` field for it to evade conflicts with other event properties.\n\nBesides, the event class describes \"what kind of event\" it is, and if the event is custom, then we should use `CustomEvent` just to be clear about what it is.\n\n## event.preventDefault()\n\nMany browser events have a \"default action\", such as navigating to a link, starting a selection, and so on.\n\nFor new, custom events, there are definitely no default browser actions, but a code that dispatches such event may have its own plans what to do after triggering the event.\n\nBy calling `event.preventDefault()`, an event handler may send a signal that those actions should be canceled.\n\nIn that case the call to `elem.dispatchEvent(event)` returns `false`. And the code that dispatched it knows that it shouldn't continue.\n\nLet's see a practical example - a hiding rabbit (could be a closing menu or something else).\n\nBelow you can see a `#rabbit` and `hide()` function that dispatches `\"hide\"` event on it, to let all interested parties know that the rabbit is going to hide.\n\nAny handler can listen for that event with `rabbit.addEventListener('hide',...)` and, if needed, cancel the action using `event.preventDefault()`. Then the rabbit won't disappear:\n\n```html run refresh autorun\n<pre id=\"rabbit\">\n  |\\   /|\n   \\|_|/\n   /. .\\\n  =\\_Y_/=\n   {>o<}\n</pre>\n<button onclick=\"hide()\">Hide()</button>\n\n<script>\n  function hide() {\n    let event = new CustomEvent(\"hide\", {\n      cancelable: true // without that flag preventDefault doesn't work\n    });\n    if (!rabbit.dispatchEvent(event)) {\n      alert('The action was prevented by a handler');\n    } else {\n      rabbit.hidden = true;\n    }\n  }\n\n  rabbit.addEventListener('hide', function(event) {\n    if (confirm(\"Call preventDefault?\")) {\n      event.preventDefault();\n    }\n  });\n</script>\n```\n\nPlease note: the event must have the flag `cancelable: true`, otherwise the call `event.preventDefault()` is ignored.\n\n## Events-in-events are synchronous\n\nUsually events are processed in a queue. That is: if the browser is processing `onclick` and a new event occurs, e.g. mouse moved, then it's handling is queued up, corresponding `mousemove` handlers will be called after `onclick` processing is finished.\n\nThe notable exception is when one event is initiated from within another one, e.g. using `dispatchEvent`. Such events are processed immediately: the new event handlers are called, and then the current event handling is resumed.\n\nFor instance, in the code below the `menu-open` event is triggered during the `onclick`.\n\nIt's processed immediately, without waiting for `onclick` handler to end:\n\n\n```html run autorun\n<button id=\"menu\">Menu (click me)</button>\n\n<script>\n  menu.onclick = function() {\n    alert(1);\n\n    menu.dispatchEvent(new CustomEvent(\"menu-open\", {\n      bubbles: true\n    }));\n\n    alert(2);\n  };\n\n  // triggers between 1 and 2\n  document.addEventListener('menu-open', () => alert('nested'));\n</script>\n```\n\nThe output order is: 1 -> nested -> 2.\n\nPlease note that the nested event `menu-open` is caught on the `document`. The propagation and handling of the nested event is finished before the processing gets back to the outer code (`onclick`).\n\nThat's not only about `dispatchEvent`, there are other cases. If an event handler calls methods that trigger other events -- they are processed synchronously too, in a nested fashion.\n\nLet's say we don't like it. We'd want `onclick` to be fully processed first, independently from `menu-open` or any other nested events.\n\nThen we can either put the `dispatchEvent` (or another event-triggering call) at the end of `onclick` or, maybe better, wrap it in the zero-delay `setTimeout`:\n\n```html run\n<button id=\"menu\">Menu (click me)</button>\n\n<script>\n  menu.onclick = function() {\n    alert(1);\n\n    setTimeout(() => menu.dispatchEvent(new CustomEvent(\"menu-open\", {\n      bubbles: true\n    })));\n\n    alert(2);\n  };\n\n  document.addEventListener('menu-open', () => alert('nested'));\n</script>\n```\n\nNow `dispatchEvent` runs asynchronously after the current code execution is finished, including `menu.onclick`, so event handlers are totally separate.\n\nThe output order becomes: 1 -> 2 -> nested.\n\n## Summary\n\nTo generate an event from code, we first need to create an event object.\n\nThe generic `Event(name, options)` constructor accepts an arbitrary event name and the `options` object with two properties:\n- `bubbles: true` if the event should bubble.\n- `cancelable: true` if the `event.preventDefault()` should work.\n\nOther constructors of native events like `MouseEvent`, `KeyboardEvent` and so on accept properties specific to that event type. For instance, `clientX` for mouse events.\n\nFor custom events we should use `CustomEvent` constructor. It has an additional option named `detail`, we should assign the event-specific data to it. Then all handlers can access it as `event.detail`.\n\nDespite the technical possibility of generating browser events like `click` or `keydown`, we should use them with great care.\n\nWe shouldn't generate browser events as it's a hacky way to run handlers. That's bad architecture most of the time.\n\nNative events might be generated:\n\n- As a dirty hack to make 3rd-party libraries work the needed way, if they don't provide other means of interaction.\n- For automated testing, to \"click the button\" in the script and see if the interface reacts correctly.\n\nCustom events with our own names are often generated for architectural purposes, to signal what happens inside our menus, sliders, carousels etc.\n"
  },
  {
    "path": "2-ui/2-events/index.md",
    "content": "# Pengenalan ke Peristiwa\n\nSebuah pengenalan ke peristiwa peramban (_browser events_), properti peristiwa (_event properties_) dan tanda-tanda penanganan (_handling patterns_).\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .selected {\n      background: #0f0;\n    }\n\n    li {\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  Click on a list item to select it.\n  <br>\n\n  <ul id=\"ul\">\n    <li>Christopher Robin</li>\n    <li>Winnie-the-Pooh</li>\n    <li>Tigger</li>\n    <li>Kanga</li>\n    <li>Rabbit. Just rabbit.</li>\n  </ul>\n\n  <script>\n    ul.onclick = function(event) {\n      if (event.target.tagName != \"LI\") return;\n\n      if (event.ctrlKey || event.metaKey) {\n        toggleSelect(event.target);\n      } else {\n        singleSelect(event.target);\n      }\n\n    }\n\n    // prevent unneeded selection of list elements on clicks\n    ul.onmousedown = function() {\n      return false;\n    };\n\n    function toggleSelect(li) {\n      li.classList.toggle('selected');\n    }\n\n    function singleSelect(li) {\n      let selected = ul.querySelectorAll('.selected');\n      for(let elem of selected) {\n        elem.classList.remove('selected');\n      }\n      li.classList.add('selected');\n    }\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .selected {\n      background: #0f0;\n    }\n\n    li {\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  Click on a list item to select it.\n  <br>\n\n  <ul id=\"ul\">\n    <li>Christopher Robin</li>\n    <li>Winnie-the-Pooh</li>\n    <li>Tigger</li>\n    <li>Kanga</li>\n    <li>Rabbit. Just rabbit.</li>\n  </ul>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/01-selectable-list/task.md",
    "content": "importance: 5\n\n---\n\n# Selectable list\n\nCreate a list where elements are selectable, like in file-managers.\n\n- A click on a list element selects only that element (adds the class `.selected`), deselects all others.\n- If a click is made with `key:Ctrl` (`key:Cmd` for Mac), then the selection is toggled on the element, but other elements are not modified.\n\nThe demo:\n\n[iframe border=\"1\" src=\"solution\" height=180]\n\nP.S. For this task we can assume that list items are text-only. No nested tags.\n\nP.P.S. Prevent the native browser selection of the text on clicks.\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/article.md",
    "content": "# Mouse events\n\nIn this chapter we'll get into more details about mouse events and their properties.\n\nPlease note: such events may come not only from \"mouse devices\", but are also from other devices, such as phones and tablets, where they are emulated for compatibility.\n\n## Mouse event types\n\nWe've already seen some of these events:\n\n`mousedown/mouseup`\n: Mouse button is clicked/released over an element.\n\n`mouseover/mouseout`\n: Mouse pointer comes over/out from an element.\n\n`mousemove`\n: Every mouse move over an element triggers that event.\n\n`click`\n: Triggers after `mousedown` and then `mouseup` over the same element if the left mouse button was used.\n\n`dblclick`\n: Triggers after two clicks on the same element within a short timeframe. Rarely used nowadays.\n\n`contextmenu`\n: Triggers when the right mouse button is pressed. There are other ways to open a context menu, e.g. using a special keyboard key, it triggers in that case also, so it's not exactly the mouse event.\n\n...There are several other events too, we'll cover them later.\n\n## Events order\n\nAs you can see from the list above, a user action may trigger multiple events.\n\nFor instance, a left-button click first triggers `mousedown`, when the button is pressed, then `mouseup` and `click` when it's released.\n\nIn cases when a single action initiates multiple events, their order is fixed. That is, the handlers are called in the order `mousedown` -> `mouseup` -> `click`.\n\n```online\nClick the button below and you'll see the events. Try double-click too.\n\nOn the teststand below all mouse events are logged, and if there is more than a 1 second delay between them they are separated by a horizontal ruler.\n\nAlso we can see the `button` property that allows to detect the mouse button, it's explained below.\n\n<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>\n```\n\n## Mouse button\n\nClick-related events always have the `button` property, which allows to get the exact mouse button.\n\nWe usually don't use it for `click` and `contextmenu` events, because the former happens only on left-click, and the latter -- only on right-click.\n\nFrom the other hand, `mousedown` and `mouseup` handlers may need `event.button`, because these events trigger on any button, so `button` allows to distinguish between \"right-mousedown\" and \"left-mousedown\".\n\nThe possible values of `event.button` are:\n\n| Button state | `event.button` |\n|--------------|----------------|\n| Left button (primary) | 0 |\n| Middle button (auxiliary) | 1 |\n| Right button (secondary) | 2 |\n| X1 button (back) | 3 |\n| X2 button (forward) | 4 |\n\nMost mouse devices only have the left and right buttons, so possible values are `0` or `2`. Touch devices also generate similar events when one taps on them.\n\nAlso there's `event.buttons` property that has all currently pressed buttons as an integer, one bit per button. In practice this property is very rarely used, you can find details at [MDN](mdn:/api/MouseEvent/buttons) if you ever need it.\n\n```warn header=\"The outdated `event.which`\"\nOld code may use `event.which` property that's an old non-standard way of getting a button, with possible values:\n\n- `event.which == 1` – left button,\n- `event.which == 2` – middle button,\n- `event.which == 3` – right button.\n\nAs of now, `event.which` is deprecated, we shouldn't use it.\n```\n\n## Modifiers: shift, alt, ctrl and meta\n\nAll mouse events include the information about pressed modifier keys.\n\nEvent properties:\n\n- `shiftKey`: `key:Shift`\n- `altKey`: `key:Alt` (or `key:Opt` for Mac)\n- `ctrlKey`: `key:Ctrl`\n- `metaKey`: `key:Cmd` for Mac\n\nThey are `true` if the corresponding key was pressed during the event.\n\nFor instance, the button below only works on `key:Alt+Shift`+click:\n\n```html autorun height=60\n<button id=\"button\">Alt+Shift+Click on me!</button>\n\n<script>\n  button.onclick = function(event) {\n*!*\n    if (event.altKey && event.shiftKey) {\n*/!*\n      alert('Hooray!');\n    }\n  };\n</script>\n```\n\n```warn header=\"Attention: on Mac it's usually `Cmd` instead of `Ctrl`\"\nOn Windows and Linux there are modifier keys `key:Alt`, `key:Shift` and `key:Ctrl`. On Mac there's one more: `key:Cmd`, corresponding to the property `metaKey`.\n\nIn most applications, when Windows/Linux uses `key:Ctrl`, on Mac `key:Cmd` is used.\n\nThat is: where a Windows user presses `key:Ctrl+Enter` or `key:Ctrl+A`, a Mac user would press `key:Cmd+Enter` or `key:Cmd+A`, and so on.\n\nSo if we want to support combinations like `key:Ctrl`+click, then for Mac it makes sense to use `key:Cmd`+click. That's more comfortable for Mac users.\n\nEven if we'd like to force Mac users to `key:Ctrl`+click -- that's kind of difficult. The problem is: a left-click with `key:Ctrl` is interpreted as a *right-click* on MacOS, and it generates the `contextmenu` event, not `click` like Windows/Linux.\n\nSo if we want users of all operating systems to feel comfortable, then together with `ctrlKey` we should check `metaKey`.\n\nFor JS-code it means that we should check `if (event.ctrlKey || event.metaKey)`.\n```\n\n```warn header=\"There are also mobile devices\"\nKeyboard combinations are good as an addition to the workflow. So that if the visitor uses a keyboard -- they work. \n\nBut if their device doesn't have it -- then there should be a way to live without modifier keys.\n```\n\n## Coordinates: clientX/Y, pageX/Y\n\nAll mouse events provide coordinates in two flavours:\n\n1. Window-relative: `clientX` and `clientY`.\n2. Document-relative: `pageX` and `pageY`.\n\nWe already covered the difference between them in the chapter <info:coordinates>.\n\nIn short, document-relative coordinates `pageX/Y` are counted from the left-upper corner of the document, and do not change when the page is scrolled, while `clientX/Y` are counted from the current window left-upper corner. When the page is scrolled, they change.\n\nFor instance, if we have a window of the size 500x500, and the mouse is in the left-upper corner, then `clientX` and `clientY` are `0`, no matter how the page is scrolled. \n\nAnd if the mouse is in the center, then `clientX` and `clientY` are `250`, no matter what place in the document it is. They are similar to `position:fixed` in that aspect.\n\n````online\nMove the mouse over the input field to see `clientX/clientY` (the example is in the `iframe`, so coordinates are relative to that `iframe`):\n\n```html autorun height=50\n<input onmousemove=\"this.value=event.clientX+':'+event.clientY\" value=\"Mouse over me\">\n```\n````\n\n## Preventing selection on mousedown\n\nDouble mouse click has a side-effect that may be disturbing in some interfaces: it selects text.\n\nFor instance, double-clicking on the text below selects it in addition to our handler:\n\n```html autorun height=50\n<span ondblclick=\"alert('dblclick')\">Double-click me</span>\n```\n\nIf one presses the left mouse button and, without releasing it, moves the mouse, that also makes the selection, often unwanted.\n\nThere are multiple ways to prevent the selection, that you can read in the chapter <info:selection-range>.\n\nIn this particular case the most reasonable way is to prevent the browser action on `mousedown`. It prevents both these selections:\n\n```html autorun height=50\nBefore...\n<b ondblclick=\"alert('Click!')\" *!*onmousedown=\"return false\"*/!*>\n  Double-click me\n</b>\n...After\n```\n\nNow the bold element is not selected on double clicks, and pressing the left button on it won't start the selection.\n\nPlease note: the text inside it is still selectable. However, the selection should start not on the text itself, but before or after it. Usually that's fine for users.\n\n````smart header=\"Preventing copying\"\nIf we want to disable selection to protect our page content from copy-pasting, then we can use another event: `oncopy`.\n\n```html autorun height=80 no-beautify\n<div *!*oncopy=\"alert('Copying forbidden!');return false\"*/!*>\n  Dear user,\n  The copying is forbidden for you.\n  If you know JS or HTML, then you can get everything from the page source though.\n</div>\n```\nIf you try to copy a piece of text in the `<div>`, that won't work, because the default action `oncopy` is prevented.\n\nSurely the user has access to HTML-source of the page, and can take the content from there, but not everyone knows how to do it.\n````\n\n## Summary\n\nMouse events have the following properties:\n\n- Button: `button`.\n- Modifier keys (`true` if pressed): `altKey`, `ctrlKey`, `shiftKey` and `metaKey` (Mac).\n  - If you want to handle `key:Ctrl`, then don't forget Mac users, they usually use `key:Cmd`, so it's better to check `if (e.metaKey || e.ctrlKey)`.\n\n- Window-relative coordinates: `clientX/clientY`.\n- Document-relative coordinates: `pageX/pageY`.\n\nThe default browser action of `mousedown` is text selection, if it's not good for the interface, then it should be prevented.\n\nIn the next chapter we'll see more details about events that follow pointer movement and how to track element changes under it.\n"
  },
  {
    "path": "2-ui/3-event-details/1-mouse-events-basics/head.html",
    "content": "<script>\n{\n  let timer = 0;\n\n  function showmesg(t, form) {\n\n     if (timer == 0) {\n       timer = new Date();\n     }\n\n     let tm = new Date();\n\n     if (tm - timer > 300) {\n       t = '------------------------------\\n' + t;\n     }\n\n     let area = document.forms[form + 'form'].getElementsByTagName('textarea')[0];\n\n     area.value += t + '\\n';\n     area.scrollTop = area.scrollHeight;\n\n     timer = tm;\n  }\n\n  function logMouse(e) {\n     let evt = e.type;\n     while (evt.length < 11) evt += ' ';\n     showmesg(evt + \" button=\" + e.button, 'test')\n     return false;\n  }\n\n  function keyval(n) {\n     if (n == null) return 'undefined';\n     let s = '' + n;\n     if (n >= 32 && n < 127) s += ' ' + String.fromCharCode(n);\n     while (s.length < 6) s += ' ';\n     return s;\n  }\n\n\n  function logClear(form) {\n  \ttimer = 0;\n  \tdocument.forms[form+'form'].getElementsByTagName('textarea')[0].value ='';\n  \tlines = 0;\n  }\n\n  window.logClear = logClear;\n  window.logMouse = logMouse;\n}\n</script>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* the tooltip should work after page scroll too */\n    }\n\n    .tooltip {\n      position: fixed;\n      z-index: 100;\n\n      padding: 10px 20px;\n\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n\n    #house {\n      margin-top: 50px;\n      width: 400px;\n      border: 1px solid brown;\n    }\n\n    #roof {\n      width: 0;\n      height: 0;\n      border-left: 200px solid transparent;\n      border-right: 200px solid transparent;\n      border-bottom: 20px solid brown;\n      margin-top: -20px;\n    }\n\n    p {\n      text-align: justify;\n      margin: 10px 3px;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div data-tooltip=\"Here is the house interior\" id=\"house\">\n    <div data-tooltip=\"Here is the roof\" id=\"roof\"></div>\n\n    <p>Once upon a time there was a mother pig who had three little pigs.</p>\n\n    <p>The three little pigs grew so big that their mother said to them, \"You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you.\"\n\n    <p>The three little pigs set off. \"We will take care that the wolf does not catch us,\" they said.</p>\n\n    <p>Soon they met a man. <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Hover over me</a></p>\n\n  </div>\n\n  <script>\n    let tooltip;\n\n    document.onmouseover = function(event) {\n      // important: a fast-moving mouse may \"jump\" right to a child on an annotated node, skipping the parent\n      // so mouseover may happen on a child.\n\n      let anchorElem = event.target.closest('[data-tooltip]');\n\n      if (!anchorElem) return;\n\n      // show tooltip and remember it\n      tooltip = showTooltip(anchorElem, anchorElem.dataset.tooltip);\n    }\n\n    document.onmouseout = function() {\n      // it is possible that mouseout triggered, but we're still inside the element\n      // (its target was inside, and it bubbled)\n      // but in this case we'll have an immediate mouseover,\n      // so the tooltip will be destroyed and shown again\n      //\n      // luckily, the \"blinking\" won't be visible,\n      // as both events happen almost at the same time\n      if (tooltip) {\n        tooltip.remove();\n        tooltip = false;\n      }\n\n    }\n\n\n    function showTooltip(anchorElem, html) {\n      let tooltipElem = document.createElement('div');\n      tooltipElem.className = 'tooltip';\n      tooltipElem.innerHTML = html;\n      document.body.append(tooltipElem);\n\n      let coords = anchorElem.getBoundingClientRect();\n\n      // position the tooltip over the center of the element\n      let left = coords.left + (anchorElem.offsetWidth - tooltipElem.offsetWidth) / 2;\n      if (left < 0) left = 0;\n\n      let top = coords.top - tooltipElem.offsetHeight - 5;\n      if (top < 0) {\n        top = coords.top + anchorElem.offsetHeight + 5;\n      }\n\n      tooltipElem.style.left = left + 'px';\n      tooltipElem.style.top = top + 'px';\n\n      return tooltipElem;\n    }\n\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    body {\n      height: 2000px;\n      /* the tooltip should work after page scroll too */\n    }\n\n    .tooltip {\n      position: fixed;\n      z-index: 100;\n\n      padding: 10px 20px;\n\n      border: 1px solid #b3c9ce;\n      border-radius: 4px;\n      text-align: center;\n      font: italic 14px/1.3 sans-serif;\n      color: #333;\n      background: #fff;\n      box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n    }\n\n    #house {\n      margin-top: 50px;\n      width: 400px;\n      border: 1px solid brown;\n    }\n\n    #roof {\n      width: 0;\n      height: 0;\n      border-left: 200px solid transparent;\n      border-right: 200px solid transparent;\n      border-bottom: 20px solid brown;\n      margin-top: -20px;\n    }\n\n    p {\n      text-align: justify;\n      margin: 10px 3px;\n    }\n  </style>\n</head>\n\n<body>\n\n\n  <div data-tooltip=\"Here is the house interior\" id=\"house\">\n    <div data-tooltip=\"Here is the roof\" id=\"roof\"></div>\n\n    <p>Once upon a time there was a mother pig who had three little pigs.</p>\n\n    <p>The three little pigs grew so big that their mother said to them, \"You are too big to live here any longer. You must go and build houses for yourselves. But take care that the wolf does not catch you.\"\n\n    <p>The three little pigs set off. \"We will take care that the wolf does not catch us,\" they said.</p>\n\n    <p>Soon they met a man. <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Hover over me</a></p>\n\n  </div>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/1-behavior-nested-tooltip/task.md",
    "content": "importance: 5\n\n---\n\n# Improved tooltip behavior\n\nWrite JavaScript that shows a tooltip over an element with the attribute `data-tooltip`. The value of this attribute should become the tooltip text.\n\nThat's like the task <info:task/behavior-tooltip>, but here the annotated elements can be nested. The most deeply nested tooltip is shown.\n\nOnly one tooltip may show up at the same time.\n\nFor instance:\n\n```html\n<div data-tooltip=\"Here – is the house interior\" id=\"house\">\n  <div data-tooltip=\"Here – is the roof\" id=\"roof\"></div>\n  ...\n  <a href=\"https://en.wikipedia.org/wiki/The_Three_Little_Pigs\" data-tooltip=\"Read on…\">Hover over me</a>\n</div>\n```\n\nThe result in iframe:\n\n[iframe src=\"solution\" height=300 border=1]\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.md",
    "content": "\nThe algorithm looks simple:\n1. Put `onmouseover/out` handlers on the element. Also can use `onmouseenter/leave` here, but they are less universal, won't work if we introduce delegation.\n2. When a mouse cursor entered the element, start measuring the speed on `mousemove`.\n3. If the speed is slow, then run `over`.\n4. When we're going out of the element, and `over` was executed, run `out`.\n\nBut how to measure the speed?\n\nThe first idea can be: run a function every `100ms` and measure the distance between previous and new coordinates. If it's small, then the speed is small.\n\nUnfortunately, there's no way to get \"current mouse coordinates\" in JavaScript. There's no function like `getCurrentMouseCoordinates()`.\n\nThe only way to get coordinates is to listen for mouse events, like `mousemove`, and take coordinates from the event object.\n\nSo let's set a handler on `mousemove` to track coordinates and remember them. And then compare them, once per `100ms`.\n\nP.S. Please note: the solution tests use `dispatchEvent` to see if the tooltip works right.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/hoverIntent.js",
    "content": "'use strict';\n\nclass HoverIntent {\n\n  constructor({\n    sensitivity = 0.1, // speed less than 0.1px/ms means \"hovering over an element\"\n    interval = 100,    // measure mouse speed once per 100ms\n    elem,\n    over,\n    out\n  }) {\n    this.sensitivity = sensitivity;\n    this.interval = interval;\n    this.elem = elem;\n    this.over = over;\n    this.out = out;\n\n    // make sure \"this\" is the object in event handlers.\n    this.onMouseMove = this.onMouseMove.bind(this);\n    this.onMouseOver = this.onMouseOver.bind(this);\n    this.onMouseOut = this.onMouseOut.bind(this);\n\n    // and in time-measuring function (called from setInterval)\n    this.trackSpeed = this.trackSpeed.bind(this);\n\n    elem.addEventListener(\"mouseover\", this.onMouseOver);\n\n    elem.addEventListener(\"mouseout\", this.onMouseOut);\n\n  }\n\n  onMouseOver(event) {\n\n    if (this.isOverElement) {\n      // if we're over the element, then ignore the event\n      // we are already measuring the speed\n      return;\n    }\n\n    this.isOverElement = true;\n\n    // after every mousemove we'll be check the distance\n    // between the previous and the current mouse coordinates\n    // if it's less than sensivity, then the speed is slow\n\n    this.prevX = event.pageX;\n    this.prevY = event.pageY;\n    this.prevTime = Date.now();\n\n    elem.addEventListener('mousemove', this.onMouseMove);\n    this.checkSpeedInterval = setInterval(this.trackSpeed, this.interval);\n  }\n\n  onMouseOut(event) {\n    // if left the element\n    if (!event.relatedTarget || !elem.contains(event.relatedTarget)) {\n      this.isOverElement = false;\n      this.elem.removeEventListener('mousemove', this.onMouseMove);\n      clearInterval(this.checkSpeedInterval);\n      if (this.isHover) {\n        // if there was a stop over the element\n        this.out.call(this.elem, event);\n        this.isHover = false;\n      }\n    }\n  }\n\n  onMouseMove(event) {\n    this.lastX = event.pageX;\n    this.lastY = event.pageY;\n    this.lastTime = Date.now();\n  }\n\n  trackSpeed() {\n\n    let speed;\n\n    if (!this.lastTime || this.lastTime == this.prevTime) {\n      // cursor didn't move\n      speed = 0;\n    } else {\n      speed = Math.sqrt(\n        Math.pow(this.prevX - this.lastX, 2) +\n        Math.pow(this.prevY - this.lastY, 2)\n      ) / (this.lastTime - this.prevTime);\n    }\n\n    if (speed < this.sensitivity) {\n      clearInterval(this.checkSpeedInterval);\n      this.isHover = true;\n      this.over.call(this.elem, event);\n    } else {\n      // speed fast, remember new coordinates as the previous ones\n      this.prevX = this.lastX;\n      this.prevY = this.lastY;\n      this.prevTime = this.lastTime;\n    }\n  }\n\n  destroy() {\n    elem.removeEventListener('mousemove', this.onMouseMove);\n    elem.removeEventListener('mouseover', this.onMouseOver);\n    elem.removeEventListener('mouseout', this.onMouseOut);\n  }\n\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"hoverIntent.js\"></script>\n  <script src=\"https://en.js.cx/test/libs.js\"></script>\n  <script src=\"test.js\"></script>\n</head>\n\n<body>\n\n  <div id=\"elem\" class=\"clock\">\n    <span class=\"hours\">12</span> :\n    <span class=\"minutes\">30</span> :\n    <span class=\"seconds\">00</span>\n  </div>\n\n  <div id=\"tooltip\" hidden>Tooltip</div>\n\n  <script>\n    new HoverIntent({\n      elem,\n      over() {\n        tooltip.style.left = elem.getBoundingClientRect().left + 5 + 'px';\n        tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n        tooltip.hidden = false;\n      },\n      out() {\n        tooltip.hidden = true;\n      }\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/style.css",
    "content": ".hours {\n  color: red;\n}\n\nbody {\n  margin: 0;\n}\n\n.minutes {\n  color: green;\n}\n\n.seconds {\n  color: blue;\n}\n\n.clock {\n  border: 1px dashed black;\n  padding: 5px;\n  display: inline-block;\n  background: yellow;\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n\n#tooltip {\n  position: absolute;\n  padding: 10px 20px;\n  border: 1px solid #b3c9ce;\n  border-radius: 4px;\n  text-align: center;\n  font: italic 14px/1.3 sans-serif;\n  color: #333;\n  background: #fff;\n  z-index: 100000;\n  box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/solution.view/test.js",
    "content": "'use strict';\n\ndescribe(\"hoverIntent\", function() {\n\n  function mouse(eventType, x, y, options) {\n    let eventOptions = Object.assign({\n      bubbles: true,\n      clientX: x,\n      clientY: y,\n      pageX: x,\n      pageY: y,\n      target: elem\n    }, options || {});\n\n    elem.dispatchEvent(new MouseEvent(eventType, eventOptions));\n  }\n\n\n  let isOver;\n  let hoverIntent;\n\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n\n  beforeEach(function() {\n    isOver = false;\n\n    hoverIntent = new HoverIntent({\n      elem: elem,\n      over: function() {\n        isOver = true;\n      },\n      out: function() {\n        isOver = false;\n      }\n    });\n  })\n\n  afterEach(function() {\n    if (hoverIntent) {\n      hoverIntent.destroy();\n    }\n  })\n\n  it(\"mouseover -> when the pointer just arrived, no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    assert.isFalse(isOver);\n  });\n\n  it(\"mouseover -> after a delay, the tooltip shows up\", function() {\n    mouse('mouseover', 10, 10);\n    this.clock.tick(100);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> followed by fast mouseout leads doesn't show tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    setTimeout(\n      () => mouse('mouseout', 300, 300, { relatedTarget: document.body}),\n      30\n    );\n    this.clock.tick(100);\n    assert.isFalse(isOver);\n  });\n\n\n  it(\"mouseover -> slow move -> tooltips\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i/5, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> fast move -> no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isFalse(isOver);\n  });\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/hoverIntent.js",
    "content": "'use strict';\n\n// Here's a brief sketch of the class\n// with things that you'll need anyway\nclass HoverIntent {\n\n  constructor({\n    sensitivity = 0.1, // speed less than 0.1px/ms means \"hovering over an element\"\n    interval = 100, // measure mouse speed once per 100ms: calculate the distance between previous and next points\n    elem,\n    over,\n    out\n  }) {\n    this.sensitivity = sensitivity;\n    this.interval = interval;\n    this.elem = elem;\n    this.over = over;\n    this.out = out;\n\n    // make sure \"this\" is the object in event handlers.\n    this.onMouseMove = this.onMouseMove.bind(this);\n    this.onMouseOver = this.onMouseOver.bind(this);\n    this.onMouseOut = this.onMouseOut.bind(this);\n\n    // assign the handlers\n    elem.addEventListener(\"mouseover\", this.onMouseOver);\n    elem.addEventListener(\"mouseout\", this.onMouseOut);\n\n    // continue from this point\n\n  }\n\n  onMouseOver(event) {\n    /* ... */\n  }\n\n  onMouseOut(event) {\n    /* ... */\n  }\n\n  onMouseMove(event) {\n    /* ... */\n  }\n\n\n  destroy() {\n    /* your code to \"disable\" the functionality, remove all handlers */\n    /* it's needed for the tests to work */\n  }\n\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"hoverIntent.js\"></script>\n  <script src=\"https://en.js.cx/test/libs.js\"></script>\n  <script src=\"test.js\"></script>\n</head>\n\n<body>\n\n  <div id=\"elem\" class=\"clock\">\n    <span class=\"hours\">12</span> :\n    <span class=\"minutes\">30</span> :\n    <span class=\"seconds\">00</span>\n  </div>\n\n  <div id=\"tooltip\" hidden>Tooltip</div>\n\n  <script>\n    new HoverIntent({\n      elem,\n      over() {\n        tooltip.style.left = elem.getBoundingClientRect().left + 5 + 'px';\n        tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n        tooltip.hidden = false;\n      },\n      out() {\n        tooltip.hidden = true;\n      }\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/style.css",
    "content": ".hours {\n  color: red;\n}\n\nbody {\n  margin: 0;\n}\n\n.minutes {\n  color: green;\n}\n\n.seconds {\n  color: blue;\n}\n\n.clock {\n  border: 1px dashed black;\n  padding: 5px;\n  display: inline-block;\n  background: yellow;\n  position: absolute;\n  left: 0;\n  top: 0;\n}\n\n#tooltip {\n  position: absolute;\n  padding: 10px 20px;\n  border: 1px solid #b3c9ce;\n  border-radius: 4px;\n  text-align: center;\n  font: italic 14px/1.3 sans-serif;\n  color: #333;\n  background: #fff;\n  z-index: 100000;\n  box-shadow: 3px 3px 3px rgba(0, 0, 0, .3);\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/source.view/test.js",
    "content": "'use strict';\n\ndescribe(\"hoverIntent\", function() {\n\n  function mouse(eventType, x, y, options) {\n    let eventOptions = Object.assign({\n      bubbles: true,\n      clientX: x,\n      clientY: y,\n      pageX: x,\n      pageY: y,\n      target: elem\n    }, options || {});\n\n    elem.dispatchEvent(new MouseEvent(eventType, eventOptions));\n  }\n\n\n  let isOver;\n  let hoverIntent;\n\n\n  before(function() {\n    this.clock = sinon.useFakeTimers();\n  });\n\n  after(function() {\n    this.clock.restore();\n  });\n\n\n  beforeEach(function() {\n    isOver = false;\n\n    hoverIntent = new HoverIntent({\n      elem: elem,\n      over: function() {\n        isOver = true;\n      },\n      out: function() {\n        isOver = false;\n      }\n    });\n  })\n\n  afterEach(function() {\n    if (hoverIntent) {\n      hoverIntent.destroy();\n    }\n  })\n\n  it(\"mouseover -> when the pointer just arrived, no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    assert.isFalse(isOver);\n  });\n\n  it(\"mouseover -> after a delay, the tooltip shows up\", function() {\n    mouse('mouseover', 10, 10);\n    this.clock.tick(100);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> followed by fast mouseout leads doesn't show tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    setTimeout(\n      () => mouse('mouseout', 300, 300, { relatedTarget: document.body}),\n      30\n    );\n    this.clock.tick(100);\n    assert.isFalse(isOver);\n  });\n\n\n  it(\"mouseover -> slow move -> tooltips\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i/5, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isTrue(isOver);\n  });\n\n  it(\"mouseover -> fast move -> no tooltip\", function() {\n    mouse('mouseover', 10, 10);\n    for(let i=10; i<200; i+= 10) {\n      setTimeout(\n        () => mouse('mousemove', i, 10),\n        i\n      );\n    }\n    this.clock.tick(200);\n    assert.isFalse(isOver);\n  });\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/2-hoverintent/task.md",
    "content": "importance: 5\n\n---\n\n# \"Smart\" tooltip\n\nWrite a function that shows a tooltip over an element only if the visitor moves the mouse *to it*, but not *through it*.\n\nIn other words, if the visitor moves the mouse to the element and stops there -- show the tooltip. And if they just moved the mouse through, then no need, who wants extra blinking?\n\nTechnically, we can measure the mouse speed over the element, and if it's slow then we assume that it comes \"over the element\" and show the tooltip, if it's fast -- then we ignore it.\n\nMake a universal object `new HoverIntent(options)` for it.\n\nIts `options`:\n- `elem` -- element to track.\n- `over` -- a function to call if the mouse came to the element: that is, it moves slowly or stopped over it.\n- `out` -- a function to call when the mouse leaves the element (if `over` was called).\n\nAn example of using such object for the tooltip:\n\n```js\n// a sample tooltip\nlet tooltip = document.createElement('div');\ntooltip.className = \"tooltip\";\ntooltip.innerHTML = \"Tooltip\";\n\n// the object will track mouse and call over/out\nnew HoverIntent({\n  elem,\n  over() {\n    tooltip.style.left = elem.getBoundingClientRect().left + 'px';\n    tooltip.style.top = elem.getBoundingClientRect().bottom + 5 + 'px';\n    document.body.append(tooltip);\n  },\n  out() {\n    tooltip.remove();\n  }\n});\n```\n\nThe demo:\n\n[iframe src=\"solution\" height=140]\n\nIf you move the mouse over the \"clock\" fast then nothing happens, and if you do it slow or stop on them, then there will be a tooltip.\n\nPlease note: the tooltip doesn't \"blink\" when the cursor moves between the clock subelements.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/article.md",
    "content": "# Moving the mouse: mouseover/out, mouseenter/leave\n\nLet's dive into more details about events that happen when the mouse moves between elements.\n\n## Events mouseover/mouseout, relatedTarget\n\nThe `mouseover` event occurs when a mouse pointer comes over an element, and `mouseout` -- when it leaves.\n\n![](mouseover-mouseout.svg)\n\nThese events are special, because they have property `relatedTarget`. This property complements `target`. When a mouse leaves one element for another, one of them becomes `target`, and the other one - `relatedTarget`.\n\nFor `mouseover`:\n\n- `event.target` -- is the element where the mouse came over.\n- `event.relatedTarget` -- is the element from which the mouse came (`relatedTarget` -> `target`).\n\nFor `mouseout` the reverse:\n\n- `event.target` -- is the element that the mouse left.\n- `event.relatedTarget` -- is the new under-the-pointer element, that mouse left for (`target` -> `relatedTarget`).\n\n```online\nIn the example below each face and its features are separate elements. When you move the mouse, you can see mouse events in the text area.\n\nEach event has the information about both `target` and `relatedTarget`:\n\n[codetabs src=\"mouseoverout\" height=280]\n```\n\n```warn header=\"`relatedTarget` can be `null`\"\nThe `relatedTarget` property can be `null`.\n\nThat's normal and just means that the mouse came not from another element, but from out of the window. Or that it left the window.\n\nWe should keep that possibility in mind when using `event.relatedTarget` in our code. If we access `event.relatedTarget.tagName`, then there will be an error.\n```\n\n## Skipping elements\n\nThe `mousemove` event triggers when the mouse moves. But that doesn't mean that every pixel leads to an event.\n\nThe browser checks the mouse position from time to time. And if it notices changes then triggers the events.\n\nThat means that if the visitor is moving the mouse very fast then some DOM-elements may be skipped:\n\n![](mouseover-mouseout-over-elems.svg)\n\nIf the mouse moves very fast from `#FROM` to `#TO` elements as painted above, then intermediate `<div>` elements (or some of them) may be skipped. The `mouseout` event may trigger on `#FROM` and then immediately `mouseover` on `#TO`.\n\nThat's good for performance, because there may be many intermediate elements. We don't really want to process in and out of each one.\n\nOn the other hand, we should keep in mind that the mouse pointer doesn't \"visit\" all elements along the way. It can \"jump\".\n\nIn particular, it's possible that the pointer jumps right inside the middle of the page from out of the window. In that case `relatedTarget` is `null`, because it came from \"nowhere\":\n\n![](mouseover-mouseout-from-outside.svg)\n\n```online\nYou can check it out \"live\" on a teststand below.\n\nIts HTML has two nested elements: the `<div id=\"child\">` is inside the `<div id=\"parent\">`. If you move the mouse fast over them, then maybe only the child div triggers events, or maybe the parent one, or maybe there will be no events at all.\n\nAlso move the pointer into the child `div`, and then move it out quickly down through the parent one. If the movement is fast enough, then the parent element is ignored. The mouse will cross the parent element without noticing it.\n\n[codetabs height=360 src=\"mouseoverout-fast\"]\n```\n\n```smart header=\"If `mouseover` triggered, there must be `mouseout`\"\nIn case of fast mouse movements, intermediate elements may be ignored, but one thing we know for sure: if the pointer \"officially\" entered an element (`mouseover` event generated), then upon leaving it we always get `mouseout`.\n```\n\n## Mouseout when leaving for a child\n\nAn important feature of `mouseout` -- it triggers, when the pointer moves from an element to its descendant, e.g. from `#parent` to `#child` in this HTML:\n\n```html\n<div id=\"parent\">\n  <div id=\"child\">...</div>\n</div>\n```\n\nIf we're on `#parent` and then move the pointer deeper into `#child`, we get `mouseout` on `#parent`!\n\n![](mouseover-to-child.svg)\n\nThat may seem strange, but can be easily explained.\n\n**According to the browser logic, the mouse cursor may be only over a *single* element at any time -- the most nested one and top by z-index.**\n\nSo if it goes to another element (even a descendant), then it leaves the previous one.\n\nPlease note another important detail of event processing.\n\nThe `mouseover` event on a descendant bubbles up. So, if `#parent` has `mouseover` handler, it triggers:\n\n![](mouseover-bubble-nested.svg)\n\n```online\nYou can see that very well in the example below: `<div id=\"child\">` is inside the `<div id=\"parent\">`. There are `mouseover/out` handlers on `#parent` element that output event details.\n\nIf you move the mouse from `#parent` to `#child`, you see two events on `#parent`:\n1. `mouseout [target: parent]` (left the parent), then\n2. `mouseover [target: child]` (came to the child, bubbled).\n\n[codetabs height=360 src=\"mouseoverout-child\"]\n```\n\nAs shown, when the pointer moves from `#parent` element to `#child`, two handlers trigger on the parent element: `mouseout` and `mouseover`:\n\n```js\nparent.onmouseout = function(event) {\n  /* event.target: parent element */\n};\nparent.onmouseover = function(event) {\n  /* event.target: child element (bubbled) */\n};\n```\n\n**If we don't examine `event.target` inside the handlers, then it may seem that the mouse pointer left `#parent` element, and then immediately came back over it.**\n\nBut that's not the case! The pointer is still over the parent, it just moved deeper into the child element.\n\nIf there are some actions upon leaving the parent element, e.g. an animation runs in `parent.onmouseout`, we usually don't want it when the pointer just goes deeper into `#parent`.\n\nTo avoid it, we can check `relatedTarget` in the handler and, if the mouse is still inside the element, then ignore such event.\n\nAlternatively we can use other events: `mouseenter` and `mouseleave`, that we'll be covering now, as they don't have such problems.\n\n## Events mouseenter and mouseleave\n\nEvents `mouseenter/mouseleave` are like `mouseover/mouseout`. They trigger when the mouse pointer enters/leaves the element.\n\nBut there are two important differences:\n\n1. Transitions inside the element, to/from descendants, are not counted.\n2. Events `mouseenter/mouseleave` do not bubble.\n\nThese events are extremely simple.\n\nWhen the pointer enters an element -- `mouseenter` triggers. The exact location of the pointer inside the element or its descendants doesn't matter.\n\nWhen the pointer leaves an element -- `mouseleave` triggers.\n\n```online\nThis example is similar to the one above, but now the top element has `mouseenter/mouseleave` instead of `mouseover/mouseout`.\n\nAs you can see, the only generated events are the ones related to moving the pointer in and out of the top element. Nothing happens when the pointer goes to the child and back. Transitions between descendants are ignored\n\n[codetabs height=340 src=\"mouseleave\"]\n```\n\n## Event delegation\n\nEvents `mouseenter/leave` are very simple and easy to use. But they do not bubble. So we can't use event delegation with them.\n\nImagine we want to handle mouse enter/leave for table cells. And there are hundreds of cells.\n\nThe natural solution would be -- to set the handler on `<table>` and process events there. But `mouseenter/leave` don't bubble. So if such event happens on `<td>`, then only a handler on that `<td>` is able to catch it.\n\nHandlers for `mouseenter/leave` on `<table>` only trigger when the pointer enters/leaves the table as a whole. It's impossible to get any information about transitions inside it.\n\nSo, let's use `mouseover/mouseout`.\n\nLet's start with simple handlers that highlight the element under mouse:\n\n```js\n// let's highlight an element under the pointer\ntable.onmouseover = function(event) {\n  let target = event.target;\n  target.style.background = 'pink';\n};\n\ntable.onmouseout = function(event) {\n  let target = event.target;\n  target.style.background = '';\n};\n```\n\n```online\nHere they are in action. As the mouse travels across the elements of this table, the current one is highlighted:\n\n[codetabs height=480 src=\"mouseenter-mouseleave-delegation\"]\n```\n\nIn our case we'd like to handle transitions between table cells `<td>`: entering a cell and leaving it. Other transitions, such as inside the cell or outside of any cells, don't interest us. Let's filter them out.\n\nHere's what we can do:\n\n- Remember the currently highlighted `<td>` in a variable, let's call it `currentElem`.\n- On `mouseover` -- ignore the event if we're still inside the current `<td>`.\n- On `mouseout` -- ignore if we didn't leave the current `<td>`.\n\nHere's an example of code that accounts for all possible situations:\n\n[js src=\"mouseenter-mouseleave-delegation-2/script.js\"]\n\nOnce again, the important features are:\n1. It uses event delegation to handle entering/leaving of any `<td>` inside the table. So it relies on `mouseover/out` instead of `mouseenter/leave` that don't bubble and hence allow no delegation.\n2. Extra events, such as moving between descendants of `<td>` are filtered out, so that `onEnter/Leave` runs only if the pointer leaves or enters `<td>` as a whole.\n\n```online\nHere's the full example with all details:\n\n[codetabs height=460 src=\"mouseenter-mouseleave-delegation-2\"]\n\nTry to move the cursor in and out of table cells and inside them. Fast or slow -- doesn't matter. Only `<td>` as a whole is highlighted, unlike the example before.\n```\n\n## Summary\n\nWe covered events `mouseover`, `mouseout`, `mousemove`, `mouseenter` and `mouseleave`.\n\nThese things are good to note:\n\n- A fast mouse move may skip intermediate elements.\n- Events `mouseover/out` and `mouseenter/leave` have an additional property: `relatedTarget`. That's the element that we are coming from/to, complementary to `target`.\n\nEvents `mouseover/out` trigger even when we go from the parent element to a child element. The browser assumes that the mouse can be only over one element at one time -- the deepest one.\n\nEvents `mouseenter/leave` are different in that aspect: they only trigger when the mouse comes in and out the element as a whole. Also they do not bubble.\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/script.js",
    "content": "// <td> under the mouse right now (if any)\nlet currentElem = null;\n\ntable.onmouseover = function(event) {\n  // before entering a new element, the mouse always leaves the previous one\n  // if currentElem is set, we didn't leave the previous <td>,\n  // that's a mouseover inside it, ignore the event\n  if (currentElem) return;\n\n  let target = event.target.closest('td');\n\n  // we moved not into a <td> - ignore\n  if (!target) return;\n\n  // moved into <td>, but outside of our table (possible in case of nested tables)\n  // ignore\n  if (!table.contains(target)) return;\n\n  // hooray! we entered a new <td>\n  currentElem = target;\n  onEnter(currentElem);\n};\n\n\ntable.onmouseout = function(event) {\n  // if we're outside of any <td> now, then ignore the event\n  // that's probably a move inside the table, but out of <td>,\n  // e.g. from <tr> to another <tr>\n  if (!currentElem) return;\n\n  // we're leaving the element – where to? Maybe to a descendant?\n  let relatedTarget = event.relatedTarget;\n\n  while (relatedTarget) {\n    // go up the parent chain and check – if we're still inside currentElem\n    // then that's an internal transition – ignore it\n    if (relatedTarget == currentElem) return;\n\n    relatedTarget = relatedTarget.parentNode;\n  }\n\n  // we left the <td>. really.\n  onLeave(currentElem);\n  currentElem = null;\n};\n\n// any functions to handle entering/leaving an element\nfunction onEnter(elem) {\n  elem.style.background = 'pink';\n\n  // show that in textarea\n  text.value += `over -> ${currentElem.tagName}.${currentElem.className}\\n`;\n  text.scrollTop = 1e6;\n}\n\nfunction onLeave(elem) {\n  elem.style.background = '';\n\n  // show that in textarea\n  text.value += `out <- ${elem.tagName}.${elem.className}\\n`;\n  text.scrollTop = 1e6;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation-2.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/script.js",
    "content": "table.onmouseover = function(event) {\n  let target = event.target;\n  target.style.background = 'pink';\n\n  text.value += `over -> ${target.tagName}\\n`;\n  text.scrollTop = text.scrollHeight;\n};\n\ntable.onmouseout = function(event) {\n  let target = event.target;\n  target.style.background = '';\n\n  text.value += `out <- ${target.tagName}\\n`;\n  text.scrollTop = text.scrollHeight;\n};\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseenter-mouseleave-delegation.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <table id=\"table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n  <textarea id=\"text\"></textarea>\n\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/script.js",
    "content": "table.onmouseenter = table.onmouseleave = log;\n\nfunction log(event) {\n  text.value += event.type + ' [target: ' + event.target.tagName + ']\\n';\n  text.scrollTop = text.scrollHeight;\n}"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave-table.view/style.css",
    "content": "#text {\n  display: block;\n  height: 100px;\n  width: 456px;\n}\n\n#table th {\n  text-align: center;\n  font-weight: bold;\n}\n\n#table td {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: bottom;\n  padding-top: 5px;\n  padding-bottom: 12px;\n  cursor: pointer;\n}\n\n#table .nw {\n  background: #999;\n}\n\n#table .n {\n  background: #03f;\n  color: #fff;\n}\n\n#table .ne {\n  background: #ff6;\n}\n\n#table .w {\n  background: #ff0;\n}\n\n#table .c {\n  background: #60c;\n  color: #fff;\n}\n\n#table .e {\n  background: #09f;\n  color: #fff;\n}\n\n#table .sw {\n  background: #963;\n  color: #fff;\n}\n\n#table .s {\n  background: #f60;\n  color: #fff;\n}\n\n#table .se {\n  background: #0c3;\n  color: #fff;\n}\n\n#table .highlight {\n  background: red;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\" onmouseenter=\"mouselog(event)\" onmouseleave=\"mouselog(event)\">parent\n    <div id=\"child\">child</div>\n  </div>\n\n  <textarea id=\"text\"></textarea>\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/script.js",
    "content": "function mouselog(event) {\n  let d = new Date();\n  text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\\n`.replace(/(:|^)(\\d\\D)/, '$10$2');\n  text.scrollTop = text.scrollHeight;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseleave.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\" onmouseover=\"mouselog(event)\" onmouseout=\"mouselog(event)\">parent\n    <div id=\"child\">child</div>\n  </div>\n\n  <textarea id=\"text\"></textarea>\n  <input type=\"button\" onclick=\"text.value=''\" value=\"Clear\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/script.js",
    "content": "function mouselog(event) {\n  let d = new Date();\n  text.value += `${d.getHours()}:${d.getMinutes()}:${d.getSeconds()} | ${event.type} [target: ${event.target.id}]\\n`.replace(/(:|^)(\\d\\D)/, '$10$2');\n  text.scrollTop = text.scrollHeight;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-child.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"parent\">parent\n    <div id=\"child\">child</div>\n  </div>\n  <textarea id=\"text\"></textarea>\n  <input onclick=\"clearText()\" value=\"Clear\" type=\"button\">\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/script.js",
    "content": "let parent = document.getElementById('parent');\nparent.onmouseover = parent.onmouseout = parent.onmousemove = handler;\n\nfunction handler(event) {\n  let type = event.type;\n  while (type < 11) type += ' ';\n\n  log(type + \" target=\" + event.target.id)\n  return false;\n}\n\n\nfunction clearText() {\n  text.value = \"\";\n  lastMessage = \"\";\n}\n\nlet lastMessageTime = 0;\nlet lastMessage = \"\";\nlet repeatCounter = 1;\n\nfunction log(message) {\n  if (lastMessageTime == 0) lastMessageTime = new Date();\n\n  let time = new Date();\n\n  if (time - lastMessageTime > 500) {\n    message = '------------------------------\\n' + message;\n  }\n\n  if (message === lastMessage) {\n    repeatCounter++;\n    if (repeatCounter == 2) {\n      text.value = text.value.trim() + ' x 2\\n';\n    } else {\n      text.value = text.value.slice(0, text.value.lastIndexOf('x') + 1) + repeatCounter + \"\\n\";\n    }\n\n  } else {\n    repeatCounter = 1;\n    text.value += message + \"\\n\";\n  }\n\n  text.scrollTop = text.scrollHeight;\n\n  lastMessageTime = time;\n  lastMessage = message;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout-fast.view/style.css",
    "content": "#parent {\n  background: #99C0C3;\n  width: 160px;\n  height: 120px;\n  position: relative;\n}\n\n#child {\n  background: #FFDE99;\n  width: 50%;\n  height: 50%;\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n\ntextarea {\n  height: 140px;\n  width: 300px;\n  display: block;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"container\">\n    <div class=\"smiley-green\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n\n    <div class=\"smiley-yellow\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n\n    <div class=\"smiley-red\">\n      <div class=\"left-eye\"></div>\n      <div class=\"right-eye\"></div>\n      <div class=\"smile\"></div>\n    </div>\n  </div>\n\n  <textarea id=\"log\">Events will show up here!\n</textarea>\n\n  <script src=\"script.js\"></script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/script.js",
    "content": "container.onmouseover = container.onmouseout = handler;\n\nfunction handler(event) {\n\n  function str(el) {\n    if (!el) return \"null\"\n    return el.className || el.tagName;\n  }\n\n  log.value += event.type + ':  ' +\n    'target=' + str(event.target) +\n    ',  relatedTarget=' + str(event.relatedTarget) + \"\\n\";\n  log.scrollTop = log.scrollHeight;\n\n  if (event.type == 'mouseover') {\n    event.target.style.background = 'pink'\n  }\n  if (event.type == 'mouseout') {\n    event.target.style.background = ''\n  }\n}\n"
  },
  {
    "path": "2-ui/3-event-details/3-mousemove-mouseover-mouseout-mouseenter-mouseleave/mouseoverout.view/style.css",
    "content": "body,\nhtml {\n  margin: 0;\n  padding: 0;\n}\n\n#container {\n  border: 1px solid brown;\n  padding: 10px;\n  width: 330px;\n  margin-bottom: 5px;\n  box-sizing: border-box;\n}\n\n#log {\n  height: 120px;\n  width: 350px;\n  display: block;\n  box-sizing: border-box;\n}\n\n[class^=\"smiley-\"] {\n  display: inline-block;\n  width: 70px;\n  height: 70px;\n  border-radius: 50%;\n  margin-right: 20px;\n}\n\n.smiley-green {\n  background: #a9db7a;\n  border: 5px solid #92c563;\n  position: relative;\n}\n\n.smiley-green .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #84b458;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-green .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #84b458;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-green .smile {\n  position: absolute;\n  top: 67%;\n  left: 16.5%;\n  width: 70%;\n  height: 20%;\n  overflow: hidden;\n}\n\n.smiley-green .smile:after,\n.smiley-green .smile:before {\n  content: \"\";\n  position: absolute;\n  top: -50%;\n  left: 0%;\n  border-radius: 50%;\n  background: #84b458;\n  height: 100%;\n  width: 97%;\n}\n\n.smiley-green .smile:after {\n  background: #84b458;\n  height: 80%;\n  top: -40%;\n  left: 0%;\n}\n\n.smiley-yellow {\n  background: #eed16a;\n  border: 5px solid #dbae51;\n  position: relative;\n}\n\n.smiley-yellow .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #dba652;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-yellow .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #dba652;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-yellow .smile {\n  position: absolute;\n  top: 67%;\n  left: 19%;\n  width: 65%;\n  height: 14%;\n  background: #dba652;\n  overflow: hidden;\n  border-radius: 8px;\n}\n\n.smiley-red {\n  background: #ee9295;\n  border: 5px solid #e27378;\n  position: relative;\n}\n\n.smiley-red .left-eye {\n  width: 18%;\n  height: 18%;\n  background: #d96065;\n  position: relative;\n  top: 29%;\n  left: 22%;\n  border-radius: 50%;\n  float: left;\n}\n\n.smiley-red .right-eye {\n  width: 18%;\n  height: 18%;\n  border-radius: 50%;\n  position: relative;\n  background: #d96065;\n  top: 29%;\n  right: 22%;\n  float: right;\n}\n\n.smiley-red .smile {\n  position: absolute;\n  top: 57%;\n  left: 16.5%;\n  width: 70%;\n  height: 20%;\n  overflow: hidden;\n}\n\n.smiley-red .smile:after,\n.smiley-red .smile:before {\n  content: \"\";\n  position: absolute;\n  top: 50%;\n  left: 0%;\n  border-radius: 50%;\n  background: #d96065;\n  height: 100%;\n  width: 97%;\n}\n\n.smiley-red .smile:after {\n  background: #d96065;\n  height: 80%;\n  top: 60%;\n  left: 0%;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.md",
    "content": "As we can see from HTML/CSS, the slider is a `<div>` with a colored background, that contains a runner -- another `<div>` with `position:relative`.\n\nTo position the runner we use `position:relative`, to provide the coordinates relative to its parent, here it's more convenient here than `position:absolute`.\n\nThen we implement horizontal-only Drag'n'Drop with limitation by width.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"slider\" class=\"slider\">\n    <div class=\"thumb\"></div>\n  </div>\n\n  <script>\n    let thumb = slider.querySelector('.thumb');\n\n    thumb.onmousedown = function(event) {\n      event.preventDefault(); // prevent selection start (browser action)\n\n      let shiftX = event.clientX - thumb.getBoundingClientRect().left;\n      // shiftY not needed, the thumb moves only horizontally\n\n      document.addEventListener('mousemove', onMouseMove);\n      document.addEventListener('mouseup', onMouseUp);\n\n      function onMouseMove(event) {\n        let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;\n\n        // the pointer is out of slider => lock the thumb within the bounaries\n        if (newLeft < 0) {\n          newLeft = 0;\n        }\n        let rightEdge = slider.offsetWidth - thumb.offsetWidth;\n        if (newLeft > rightEdge) {\n          newLeft = rightEdge;\n        }\n\n        thumb.style.left = newLeft + 'px';\n      }\n\n      function onMouseUp() {\n        document.removeEventListener('mouseup', onMouseUp);\n        document.removeEventListener('mousemove', onMouseMove);\n      }\n\n    };\n\n    thumb.ondragstart = function() {\n      return false;\n    };\n\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/solution.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"slider\" class=\"slider\">\n    <div class=\"thumb\"></div>\n  </div>\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/source.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/1-slider/task.md",
    "content": "importance: 5\n\n---\n\n# Slider\n\nCreate a slider:\n\n[iframe src=\"solution\" height=60 border=1]\n\nDrag the blue thumb with the mouse and move it.\n\nImportant details:\n\n- When the mouse button is pressed, during the dragging the mouse may go over or below the slider. The slider will still work (convenient for the user).\n- If the mouse moves very fast to the left or to the right, the thumb should stop exactly at the edge.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.md",
    "content": "To drag the element we can use `position:fixed`, it makes coordinates easier to manage. At the end we should switch it back to `position:absolute` to lay the element into the document.\n\nWhen coordinates are at window top/bottom, we use `window.scrollTo` to scroll it.\n\nMore details in the code, in comments.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"soccer.css\">\n</head>\n\n<body>\n\n  <h2>Place superheroes around the soccer field.</h2>\n\n  <p>Superheroes and the ball are elements with the class \"draggable\". Make them really draggable.</p>\n\n  <p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p>\n\n  <p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p>\n\n  <p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p>\n\n  <p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p>\n\n  <div id=\"field\">\n\n  </div>\n\n  <div class=\"hero draggable\" id=\"hero1\"></div>\n  <div class=\"hero draggable\" id=\"hero2\"></div>\n  <div class=\"hero draggable\" id=\"hero3\"></div>\n  <div class=\"hero draggable\" id=\"hero4\"></div>\n  <div class=\"hero draggable\" id=\"hero5\"></div>\n  <div class=\"hero draggable\" id=\"hero6\"></div>\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" class=\"draggable\">\n\n  <div style=\"clear:both\"></div>\n\n  <script src=\"soccer.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n}\n\n#field {\n  background: url(field.svg);\n  width: 800px;\n  height: 500px;\n  float: left;\n}\n\n/* heroes and the ball (dragables) */\n\n.hero {\n  background: url(https://js.cx/drag-heroes/heroes.png);\n  width: 130px;\n  height: 128px;\n  float: left;\n}\n\n#hero1 {\n  background-position: 0 0;\n}\n\n#hero2 {\n  background-position: 0 -128px;\n}\n\n#hero3 {\n  background-position: -120px 0;\n}\n\n#hero4 {\n  background-position: -125px -128px;\n}\n\n#hero5 {\n  background-position: -248px -128px;\n}\n\n#hero6 {\n  background-position: -244px 0;\n}\n\n.draggable {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/solution.view/soccer.js",
    "content": "let isDragging = false;\n\ndocument.addEventListener('mousedown', function(event) {\n\n  let dragElement = event.target.closest('.draggable');\n\n  if (!dragElement) return;\n\n  event.preventDefault();\n\n  dragElement.ondragstart = function() {\n      return false;\n  };\n\n  let coords, shiftX, shiftY;\n\n  startDrag(dragElement, event.clientX, event.clientY);\n\n  function onMouseUp(event) {\n    finishDrag();\n  };\n\n  function onMouseMove(event) {\n    moveAt(event.clientX, event.clientY);\n  }\n\n  // on drag start:\n  //   remember the initial shift\n  //   move the element position:fixed and a direct child of body\n  function startDrag(element, clientX, clientY) {\n    if(isDragging) {\n      return;\n    }\n\n    isDragging = true;\n\n    document.addEventListener('mousemove', onMouseMove);\n    element.addEventListener('mouseup', onMouseUp);\n\n    shiftX = clientX - element.getBoundingClientRect().left;\n    shiftY = clientY - element.getBoundingClientRect().top;\n\n    element.style.position = 'fixed';\n\n    moveAt(clientX, clientY);\n  };\n\n  // switch to absolute coordinates at the end, to fix the element in the document\n  function finishDrag() {\n    if(!isDragging) {\n      return;\n    }\n\n    isDragging = false;\n\n    dragElement.style.top = parseInt(dragElement.style.top) + window.pageYOffset + 'px';\n    dragElement.style.position = 'absolute';\n\n    document.removeEventListener('mousemove', onMouseMove);\n    dragElement.removeEventListener('mouseup', onMouseUp);\n  }\n\n  function moveAt(clientX, clientY) {\n    // new window-relative coordinates\n    let newX = clientX - shiftX;\n    let newY = clientY - shiftY;\n\n    // check if the new coordinates are below the bottom window edge\n    let newBottom = newY + dragElement.offsetHeight; // new bottom\n\n    // below the window? let's scroll the page\n    if (newBottom > document.documentElement.clientHeight) {\n      // window-relative coordinate of document end\n      let docBottom = document.documentElement.getBoundingClientRect().bottom;\n\n      // scroll the document down by 10px has a problem\n      // it can scroll beyond the end of the document\n      // Math.min(how much left to the end, 10)\n      let scrollY = Math.min(docBottom - newBottom, 10);\n\n      // calculations are imprecise, there may be rounding errors that lead to scrolling up\n      // that should be impossible, fix that here\n      if (scrollY < 0) scrollY = 0;\n\n      window.scrollBy(0, scrollY);\n\n      // a swift mouse move make put the cursor beyond the document end\n      // if that happens -\n      // limit the new Y by the maximally possible (right at the bottom of the document)\n      newY = Math.min(newY, document.documentElement.clientHeight - dragElement.offsetHeight);\n    }\n\n    // check if the new coordinates are above the top window edge (similar logic)\n    if (newY < 0) {\n      // scroll up\n      let scrollY = Math.min(-newY, 10);\n      if (scrollY < 0) scrollY = 0; // check precision errors\n\n      window.scrollBy(0, -scrollY);\n      // a swift mouse move can put the cursor beyond the document start\n      newY = Math.max(newY, 0); // newY may not be below 0\n    }\n\n\n    // limit the new X within the window boundaries\n    // there's no scroll here so it's simple\n    if (newX < 0) newX = 0;\n    if (newX > document.documentElement.clientWidth - dragElement.offsetWidth) {\n      newX = document.documentElement.clientWidth - dragElement.offsetWidth;\n    }\n\n    dragElement.style.left = newX + 'px';\n    dragElement.style.top = newY + 'px';\n  }\n\n});\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"soccer.css\">\n</head>\n\n<body>\n\n  <h2>Place superheroes around the soccer field.</h2>\n\n  <p>Superheroes and the ball are elements with the class \"draggable\". Make them really draggable.</p>\n\n  <p>Important: limit dragging by the window. If a draggable reaches window top or bottom, then the page should scroll to let us drag it further.</p>\n\n  <p>If your screen is big enough to fit the whole document -- make the window smaller to get vertical scrolling, so that you could test it.</p>\n\n  <p>In this task it's enough to handle vertical scrolling. There's no horizontal scrolling usually, and it's handled the similar way if needed.</p>\n\n  <p>And one more thing: heroes may never leave the page. If they reach the edge of the document, no dragging outside of it.</p>\n\n  <div id=\"field\">\n\n  </div>\n\n  <div class=\"hero draggable\" id=\"hero1\"></div>\n  <div class=\"hero draggable\" id=\"hero2\"></div>\n  <div class=\"hero draggable\" id=\"hero3\"></div>\n  <div class=\"hero draggable\" id=\"hero4\"></div>\n  <div class=\"hero draggable\" id=\"hero5\"></div>\n  <div class=\"hero draggable\" id=\"hero6\"></div>\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" class=\"draggable\">\n\n  <div style=\"clear:both\"></div>\n\n  <script src=\"soccer.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/soccer.css",
    "content": "html, body {\n  margin: 0;\n  padding: 0;\n}\n\n#field {\n  background: url(field.svg);\n  width: 800px;\n  height: 500px;\n  float: left;\n}\n\n/* heroes and the ball (dragables) */\n\n.hero {\n  background: url(https://js.cx/drag-heroes/heroes.png);\n  width: 130px;\n  height: 128px;\n  float: left;\n}\n\n#hero1 {\n  background-position: 0 0;\n}\n\n#hero2 {\n  background-position: 0 -128px;\n}\n\n#hero3 {\n  background-position: -120px 0;\n}\n\n#hero4 {\n  background-position: -125px -128px;\n}\n\n#hero5 {\n  background-position: -248px -128px;\n}\n\n#hero6 {\n  background-position: -244px 0;\n}\n\n.draggable {\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/source.view/soccer.js",
    "content": "// Your code\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/2-drag-heroes/task.md",
    "content": "importance: 5\n\n---\n\n# Drag superheroes around the field\n\nThis task can help you to check understanding of several aspects of Drag'n'Drop and DOM.\n\nMake all elements with class `draggable` -- draggable. Like a ball in the chapter.\n\nRequirements:\n\n- Use event delegation to track drag start: a single event handler on `document` for `mousedown`.\n- If elements are dragged to top/bottom window edges -- the page scrolls up/down to allow further dragging.\n- There is no horizontal scroll (this makes the task a bit simpler, adding it is easy).\n- Draggable elements or their parts should never leave the window, even after swift mouse moves.\n\nThe demo is too big to fit it here, so here's the link.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/article.md",
    "content": "# Drag'n'Drop with mouse events\n\nDrag'n'Drop is a great interface solution. Taking something and dragging and dropping it is a clear and simple way to do many things, from copying and moving documents (as in file managers) to ordering (dropping items into a cart).\n\nIn the modern HTML standard there's a [section about Drag and Drop](https://html.spec.whatwg.org/multipage/interaction.html#dnd) with special events such as `dragstart`, `dragend`, and so on.\n\nThese events allow us to support special kinds of drag'n'drop, such as handling dragging a file from OS file-manager and dropping it into the browser window. Then JavaScript can access the contents of such files.\n\nBut native Drag Events also have limitations. For instance, we can't prevent dragging from a certain area. Also we can't make the dragging \"horizontal\" or \"vertical\" only. And there are many other drag'n'drop tasks that can't be done using them. Also, mobile device support for such events is very weak.\n\nSo here we'll see how to implement Drag'n'Drop using mouse events.\n\n## Drag'n'Drop algorithm\n\nThe basic Drag'n'Drop algorithm looks like this:\n\n1. On `mousedown` - prepare the element for moving, if needed (maybe create a clone of it, add a class to it or whatever).\n2. Then on `mousemove` move it by changing `left/top` with `position:absolute`.\n3. On `mouseup` - perform all actions related to finishing the drag'n'drop.\n\nThese are the basics. Later we'll see how to other features, such as highlighting current underlying elements while we drag over them.\n\nHere's the implementation of dragging a ball:\n\n```js\nball.onmousedown = function(event) { \n  // (1) prepare to moving: make absolute and on top by z-index\n  ball.style.position = 'absolute';\n  ball.style.zIndex = 1000;\n\n  // move it out of any current parents directly into body\n  // to make it positioned relative to the body\n  document.body.append(ball);  \n\n  // centers the ball at (pageX, pageY) coordinates\n  function moveAt(pageX, pageY) {\n    ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n    ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n  }\n\n  // move our absolutely positioned ball under the pointer\n  moveAt(event.pageX, event.pageY);\n\n  function onMouseMove(event) {\n    moveAt(event.pageX, event.pageY);\n  }\n\n  // (2) move the ball on mousemove\n  document.addEventListener('mousemove', onMouseMove);\n\n  // (3) drop the ball, remove unneeded handlers\n  ball.onmouseup = function() {\n    document.removeEventListener('mousemove', onMouseMove);\n    ball.onmouseup = null;\n  };\n\n};\n```\n\nIf we run the code, we can notice something strange. On the beginning of the drag'n'drop, the ball \"forks\": we start dragging its \"clone\".\n\n```online\nHere's an example in action:\n\n[iframe src=\"ball\" height=230]\n\nTry to drag'n'drop with the mouse and you'll see such behavior.\n```\n\nThat's because the browser has its own drag'n'drop support for images and some other elements. It runs automatically and conflicts with ours.\n\nTo disable it:\n\n```js\nball.ondragstart = function() {\n  return false;\n};\n```\n\nNow everything will be all right.\n\n```online\nIn action:\n\n[iframe src=\"ball2\" height=230]\n```\n\nAnother important aspect -- we track `mousemove` on `document`, not on `ball`. From the first sight it may seem that the mouse is always over the ball, and we can put `mousemove` on it.\n\nBut as we remember, `mousemove` triggers often, but not for every pixel. So after swift move the pointer can jump from the ball somewhere in the middle of document (or even outside of the window).\n\nSo we should listen on `document` to catch it.\n\n## Correct positioning\n\nIn the examples above the ball is always moved so, that it's center is under the pointer:\n\n```js\nball.style.left = pageX - ball.offsetWidth / 2 + 'px';\nball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n```\n\nNot bad, but there's a side-effect. To initiate the drag'n'drop, we can `mousedown` anywhere on the ball. But if \"take\" it from its edge, then the ball suddenly \"jumps\" to become centered under the mouse pointer.\n\nIt would be better if we keep the initial shift of the element relative to the pointer.\n\nFor instance, if we start dragging by the edge of the ball, then the pointer should remain over the edge while dragging.\n\n![](ball_shift.svg)\n\nLet's update our algorithm:\n\n1. When a visitor presses the button (`mousedown`) - remember the distance from the pointer to the left-upper corner of the ball in variables `shiftX/shiftY`. We'll keep that distance while dragging.\n\n    To get these shifts we can substract the coordinates:\n\n    ```js\n    // onmousedown\n    let shiftX = event.clientX - ball.getBoundingClientRect().left;\n    let shiftY = event.clientY - ball.getBoundingClientRect().top;\n    ```\n\n2. Then while dragging we position the ball on the same shift relative to the pointer, like this:\n\n    ```js\n    // onmousemove\n    // ball has position:absolute\n    ball.style.left = event.pageX - *!*shiftX*/!* + 'px';\n    ball.style.top = event.pageY - *!*shiftY*/!* + 'px';\n    ```\n\nThe final code with better positioning:\n\n```js\nball.onmousedown = function(event) {\n\n*!*\n  let shiftX = event.clientX - ball.getBoundingClientRect().left;\n  let shiftY = event.clientY - ball.getBoundingClientRect().top;\n*/!*\n\n  ball.style.position = 'absolute';\n  ball.style.zIndex = 1000;\n  document.body.append(ball);\n\n  moveAt(event.pageX, event.pageY);\n\n  // moves the ball at (pageX, pageY) coordinates\n  // taking initial shifts into account\n  function moveAt(pageX, pageY) {\n    ball.style.left = pageX - *!*shiftX*/!* + 'px';\n    ball.style.top = pageY - *!*shiftY*/!* + 'px';\n  }\n\n  function onMouseMove(event) {\n    moveAt(event.pageX, event.pageY);\n  }\n\n  // move the ball on mousemove\n  document.addEventListener('mousemove', onMouseMove);\n\n  // drop the ball, remove unneeded handlers\n  ball.onmouseup = function() {\n    document.removeEventListener('mousemove', onMouseMove);\n    ball.onmouseup = null;\n  };\n\n};\n\nball.ondragstart = function() {\n  return false;\n};\n```\n\n```online\nIn action (inside `<iframe>`):\n\n[iframe src=\"ball3\" height=230]\n```\n\nThe difference is especially noticeable if we drag the ball by its right-bottom corner. In the previous example the ball \"jumps\" under the pointer. Now it fluently follows the pointer from the current position.\n\n## Potential drop targets (droppables)\n\nIn previous examples the ball could be dropped just \"anywhere\" to stay. In real-life we usually take one element and drop it onto another. For instance, a \"file\" into a \"folder\" or something else.\n\nSpeaking abstract, we take a \"draggable\" element and drop it onto \"droppable\" element.\n\nWe need to know:\n- where the element was dropped at the end of Drag'n'Drop -- to do the corresponding action,\n- and, preferably, know the droppable we're dragging over, to highlight it.\n\nThe solution is kind-of interesting and just a little bit tricky, so let's cover it here.\n\nWhat may be the first idea? Probably to set `mouseover/mouseup` handlers on potential droppables?\n\nBut that doesn't work.\n\nThe problem is that, while we're dragging, the draggable element is always above other elements. And mouse events only happen on the top element, not on those below it.\n\nFor instance, below are two `<div>` elements, red one on top of the blue one (fully covers). There's no way to catch an event on the blue one, because the red is on top:\n\n```html run autorun height=60\n<style>\n  div {\n    width: 50px;\n    height: 50px;\n    position: absolute;\n    top: 0;\n  }\n</style>\n<div style=\"background:blue\" onmouseover=\"alert('never works')\"></div>\n<div style=\"background:red\" onmouseover=\"alert('over red!')\"></div>\n```\n\nThe same with a draggable element. The ball is always on top over other elements, so events happen on it. Whatever handlers we set on lower elements, they won't work.\n\nThat's why the initial idea to put handlers on potential droppables doesn't work in practice. They won't run.\n\nSo, what to do?\n\nThere's a method called `document.elementFromPoint(clientX, clientY)`. It returns the most nested element on given window-relative coordinates (or `null` if given coordinates are out of the window).\n\nWe can use it in any of our mouse event handlers to detect the potential droppable under the pointer, like this:\n\n```js\n// in a mouse event handler\nball.hidden = true; // (*) hide the element that we drag\n\nlet elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n// elemBelow is the element below the ball, may be droppable\n\nball.hidden = false;\n```\n\nPlease note: we need to hide the ball before the call `(*)`. Otherwise we'll usually have a ball on these coordinates, as it's the top element under the pointer: `elemBelow=ball`. So we hide it and immediately show again.\n\nWe can use that code to check what element we're \"flying over\" at any time. And handle the drop when it happens.\n\nAn extended code of `onMouseMove` to find \"droppable\" elements:\n\n```js\n// potential droppable that we're flying over right now\nlet currentDroppable = null;\n\nfunction onMouseMove(event) {\n  moveAt(event.pageX, event.pageY);\n\n  ball.hidden = true;\n  let elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n  ball.hidden = false;\n\n  // mousemove events may trigger out of the window (when the ball is dragged off-screen)\n  // if clientX/clientY are out of the window, then elementFromPoint returns null\n  if (!elemBelow) return;\n\n  // potential droppables are labeled with the class \"droppable\" (can be other logic)\n  let droppableBelow = elemBelow.closest('.droppable');\n\n  if (currentDroppable != droppableBelow) {\n    // we're flying in or out...\n    // note: both values can be null\n    //   currentDroppable=null if we were not over a droppable before this event (e.g over an empty space)\n    //   droppableBelow=null if we're not over a droppable now, during this event\n\n    if (currentDroppable) {\n      // the logic to process \"flying out\" of the droppable (remove highlight)\n      leaveDroppable(currentDroppable);\n    }\n    currentDroppable = droppableBelow;\n    if (currentDroppable) {\n      // the logic to process \"flying in\" of the droppable\n      enterDroppable(currentDroppable);\n    }\n  }\n}\n```\n\nIn the example below when the ball is dragged over the soccer goal, the goal is highlighted.\n\n[codetabs height=250 src=\"ball4\"]\n\nNow we have the current \"drop target\", that we're flying over, in the variable `currentDroppable` during the whole process and can use it to highlight or any other stuff.\n\n## Summary\n\nWe considered a basic Drag'n'Drop algorithm.\n\nThe key components:\n\n1. Events flow: `ball.mousedown` -> `document.mousemove` -> `ball.mouseup` (don't forget to cancel native `ondragstart`).\n2. At the drag start -- remember the initial shift of the pointer relative to the element: `shiftX/shiftY` and keep it during the dragging.\n3. Detect droppable elements under the pointer using `document.elementFromPoint`.\n\nWe can lay a lot on this foundation.\n\n- On `mouseup` we can intellectually finalize the drop: change data, move elements around.\n- We can highlight the elements we're flying over.\n- We can limit dragging by a certain area or direction.\n- 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.\n- And so on.\n\nThere are frameworks that build architecture over it: `DragZone`, `Droppable`, `Draggable` and other classes. Most of them do the similar stuff to what's described above, so it should be easy to understand them now. Or roll your own, as you can see that that's easy enough to do, sometimes easier than adapting a third-party solution.\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.appendChild(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n        ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball2.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.appendChild(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - ball.offsetWidth / 2 + 'px';\n        ball.style.top = pageY - ball.offsetHeight / 2 + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball3.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body style=\"height: 200px\">\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n\n  <script>\n    ball.onmousedown = function(event) {\n\n      let shiftX = event.clientX - ball.getBoundingClientRect().left;\n      let shiftY = event.clientY - ball.getBoundingClientRect().top;\n\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.append(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - shiftX + 'px';\n        ball.style.top = pageY - shiftY + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://en.js.cx/clipart/soccer-gate.svg\" id=\"gate\" class=\"droppable\">\n\n  <img src=\"https://en.js.cx/clipart/ball.svg\" id=\"ball\">\n\n  <script>\n    let currentDroppable = null;\n\n    ball.onmousedown = function(event) {\n\n      let shiftX = event.clientX - ball.getBoundingClientRect().left;\n      let shiftY = event.clientY - ball.getBoundingClientRect().top;\n\n      ball.style.position = 'absolute';\n      ball.style.zIndex = 1000;\n      document.body.append(ball);\n\n      moveAt(event.pageX, event.pageY);\n\n      function moveAt(pageX, pageY) {\n        ball.style.left = pageX - shiftX + 'px';\n        ball.style.top = pageY - shiftY + 'px';\n      }\n\n      function onMouseMove(event) {\n        moveAt(event.pageX, event.pageY);\n\n        ball.hidden = true;\n        let elemBelow = document.elementFromPoint(event.clientX, event.clientY);\n        ball.hidden = false;\n\n        if (!elemBelow) return;\n\n        let droppableBelow = elemBelow.closest('.droppable');\n        if (currentDroppable != droppableBelow) {\n          if (currentDroppable) { // null when we were not over a droppable before this event\n            leaveDroppable(currentDroppable);\n          }\n          currentDroppable = droppableBelow;\n          if (currentDroppable) { // null if we're not coming over a droppable now\n            // (maybe just left the droppable)\n            enterDroppable(currentDroppable);\n          }\n        }\n      }\n\n      document.addEventListener('mousemove', onMouseMove);\n\n      ball.onmouseup = function() {\n        document.removeEventListener('mousemove', onMouseMove);\n        ball.onmouseup = null;\n      };\n\n    };\n\n    function enterDroppable(elem) {\n      elem.style.background = 'pink';\n    }\n\n    function leaveDroppable(elem) {\n      elem.style.background = '';\n    }\n\n    ball.ondragstart = function() {\n      return false;\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/4-mouse-drag-and-drop/ball4.view/style.css",
    "content": "\n#gate {\n  cursor: pointer;\n  margin-bottom: 100px;\n  width: 83px;\n  height: 46px;\n}\n\n#ball {\n  cursor: pointer;\n  width: 40px;\n  height: 40px;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/article.md",
    "content": "# Pointer events\n\nPointer events are a modern way to handle input from a variety of pointing devices, such as a mouse, a pen/stylus, a touchscreen, and so on.\n\n## The brief history\n\nLet's make a small overview, so that you understand the general picture and the place of Pointer Events among other event types.\n\n- Long ago, in the past, there were only mouse events.\n\n    Then touch devices became widespread, phones and tablets in particular. For the existing scripts to work, they generated (and still generate) mouse events. For instance, tapping a touchscreen generates `mousedown`. So touch devices worked well with web pages.\n    \n    But touch devices have more capabilities than a mouse. For example, it's possible to touch multiple points at once (\"multi-touch\"). Although, mouse events don't have necessary properties to handle such multi-touches.\n\n- So touch events were introduced, such as `touchstart`, `touchend`, `touchmove`, that have touch-specific properties (we don't cover them in detail here, because pointer events are even better).\n\n    Still, it wasn't enough, as there are many other devices, such as pens, that have their own features. Also, writing code that listens for both touch and mouse events was cumbersome. \n\n- To solve these issues, the new standard Pointer Events was introduced. It provides a single set of events for all kinds of pointing devices.\n\nAs of now, [Pointer Events Level 2](https://www.w3.org/TR/pointerevents2/) specification is supported in all major browsers, while the newer [Pointer Events Level 3](https://w3c.github.io/pointerevents/) is in the works and is mostly compartible with Pointer Events level 2. \n\nUnless you develop for old browsers, such as Internet Explorer 10, or for Safari 12 or below, there's no point in using mouse or touch events any more -- we can switch to pointer events.\n\nThen your code will work well with both touch and mouse devices.\n\nThat said, there are some important peculiarities that one should know in order to use Pointer Events correctly and avoid surprises. We'll make note of them in this article.\n\n## Pointer event types\n\nPointer events are named similarly to mouse events:\n\n| Pointer event | Similar mouse event |\n|---------------|-------------|\n| `pointerdown` | `mousedown` |\n| `pointerup` | `mouseup` |\n| `pointermove` | `mousemove` |\n| `pointerover` | `mouseover` |\n| `pointerout` | `mouseout` |\n| `pointerenter` | `mouseenter` |\n| `pointerleave` | `mouseleave` |\n| `pointercancel` | - |\n| `gotpointercapture` | - |\n| `lostpointercapture` | - |\n\nAs we can see, for every `mouse<event>`, there's a `pointer<event>` that plays a similar role. Also there are 3 additional pointer events that don't have a corresponding `mouse...` counterpart, we'll explain them soon. \n\n```smart header=\"Replacing `mouse<event>` with `pointer<event>` in our code\"\nWe can replace `mouse<event>` events with `pointer<event>` in our code and expect things to continue working fine with mouse.\n\nThe support for touch devices will also \"magically\" improve. Although, we may need to add `touch-action: none` in some places in CSS. We'll cover it below in the section about `pointercancel`. \n```\n\n## Pointer event properties\n\nPointer events have the same properties as mouse events, such as `clientX/Y`, `target`, etc., plus some others:\n\n- `pointerId` - the unique identifier of the pointer causing the event.\n    \n    Browser-generated. Allows us to handle multiple pointers, such as a touchscreen with stylus and multi-touch (examples will follow).\n- `pointerType` - the pointing device type. Must be a string, one of: \"mouse\", \"pen\" or \"touch\". \n\n    We can use this property to react differently on various pointer types.\n- `isPrimary` - is `true` for the primary pointer (the first finger in multi-touch).\n\nSome pointer devices measure contact area and pressure, e.g. for a finger on the touchscreen, there are additional properties for that:\n\n- `width` - the width of the area where the pointer (e.g. a finger) touches the device. Where unsupported, e.g. for a mouse, it's always `1`. \n- `height` - the height of the area where the pointer touches the device. Where unsupported, it's always `1`.\n- `pressure` - the pressure of the pointer tip, in range from 0 to 1. For devices that don't support pressure must be either `0.5` (pressed) or `0`.\n- `tangentialPressure` - the normalized tangential pressure.\n- `tiltX`, `tiltY`, `twist` - pen-specific properties that describe how the pen is positioned relative the surface.\n\nThese properties aren't supported by most devices, so they are rarely used. You can find the details about them in the [specification](https://w3c.github.io/pointerevents/#pointerevent-interface) if needed.\n\n## Multi-touch\n\nOne of the things that mouse events totally don't support is multi-touch: a user can touch in several places at once on their phone or tablet, or perform special gestures.\n\nPointer Events allow handling multi-touch with the help of the `pointerId` and `isPrimary` properties.\n\nHere's what happens when a user touches a touchscreen in one place, then puts another finger somewhere else on it:\n\n1. At the first finger touch:\n    - `pointerdown` with `isPrimary=true` and some `pointerId`.\n2. For the second finger and more fingers (assuming the first one is still touching):\n    - `pointerdown` with `isPrimary=false` and a different `pointerId` for every finger.\n\nPlease note: the `pointerId` is assigned not to the whole device, but for each touching finger. If we use 5 fingers to simultaneously touch the screen, we have 5 `pointerdown` events, each with their respective coordinates and a different `pointerId`.\n\nThe events associated with the first finger always have `isPrimary=true`.\n\nWe can track multiple touching fingers using their `pointerId`. When the user moves and then removes a finger, we get `pointermove` and `pointerup` events with the same `pointerId` as we had in `pointerdown`.\n\n```online\nHere's the demo that logs `pointerdown` and `pointerup` events:\n\n[iframe src=\"multitouch\" edit height=200]\n\nPlease note: you must be using a touchscreen device, such as a phone or a tablet, to actually see the difference in `pointerId/isPrimary`. For single-touch devices, such as a mouse, there'll be always same `pointerId` with `isPrimary=true`, for all pointer events.\n```\n\n## Event: pointercancel\n\nThe `pointercancel` event fires when there's an ongoing pointer interaction, and then something happens that causes it to be aborted, so that no more pointer events are generated. \n\nSuch causes are: \n- The pointer device hardware was physically disabled.\n- The device orientation changed (tablet rotated). \n- The browser decided to handle the interaction on its own, considering it a mouse gesture or zoom-and-pan action or something else.\n\nWe'll demonstrate `pointercancel` on a practical example to see how it affects us.\n\nLet's say we're impelementing drag'n'drop for a ball, just as in the beginning of the article <info:mouse-drag-and-drop>.\n\nHere is the flow of user actions and the corresponding events:\n\n1) The user presses on an image, to start dragging\n    - `pointerdown` event fires\n2) Then they start moving the pointer (thus dragging the image)\n    - `pointermove` fires, maybe several times\n3) And then the surprise happens! The browser has native drag'n'drop support for images, that kicks in and takes over the drag'n'drop process, thus generating `pointercancel` event.\n    - The browser now handles drag'n'drop of the image on its own. The user may even drag the ball image out of the browser, into their Mail program or a File Manager.\n    - No more `pointermove` events for us.\n\nSo the issue is that the browser \"hijacks\" the interaction: `pointercancel` fires in the beginning of the \"drag-and-drop\" process, and no more `pointermove` events are generated.\n\n```online\nHere's the drag'n'drop demo with loggin of pointer events (only `up/down`, `move` and `cancel`) in the `textarea`: \n\n[iframe src=\"ball\" height=240 edit]\n```\n\nWe'd like to implement the drag'n'drop on our own, so let's tell the browser not to take it over.\n\n**Prevent the default browser action to avoid `pointercancel`.**\n\nWe need to do two things:\n\n1. Prevent native drag'n'drop from happening:\n    - We can do this by setting `ball.ondragstart = () => false`, just as described in the article <info:mouse-drag-and-drop>.\n    - That works well for mouse events.\n2. For touch devices, there are other touch-related browser actions (besides drag'n'drop). To avoid problems with them too:\n    - Prevent them by setting `#ball { touch-action: none }` in CSS. \n    - Then our code will start working on touch devices.\n\nAfter we do that, the events will work as intended, the browser won't hijack the process and doesn't emit `pointercancel`.\n\n```online\nThis demo adds these lines:\n\n[iframe src=\"ball-2\" height=240 edit]\n\nAs you can see, there's no `pointercancel` any more.\n```\n\nNow we can add the code to actually move the ball, and our drag'n'drop will work for mouse devices and touch devices.\n\n## Pointer capturing\n\nPointer capturing is a special feature of pointer events.\n\nThe idea is very simple, but may seem quite odd at first, as nothing like that exists for any other event type.\n\nThe main method is:\n- `elem.setPointerCapture(pointerId)` - binds events with the given `pointerId` to `elem`. After the call all pointer events with the same `pointerId` will have `elem` as the target (as if happened on `elem`), no matter where in document they really happened.\n\nIn other words, `elem.setPointerCapture(pointerId)` retargets all subsequent events with the given `pointerId` to `elem`.\n\nThe binding is removed:\n- automatically when `pointerup` or `pointercancel` events occur,\n- automatically when `elem` is removed from the document,\n- when `elem.releasePointerCapture(pointerId)` is called.\n\n**Pointer capturing can be used to simplify drag'n'drop kind of interactions.**\n\nAs an example, let's recall how one can implement a custom slider, described in the <info:mouse-drag-and-drop>.\n\nWe make a slider element with the strip and the \"runner\" (`thumb`) inside it.\n\nThen it works like this:\n\n1. The user presses on the slider `thumb` - `pointerdown` triggers.\n2. Then they move the pointer - `pointermove` triggers, and we move the `thumb` along.\n    - ...As the pointer moves, it may leave the slider `thumb`: go above or below it. The `thumb` should move strictly horizontally, remaining aligned with the pointer.\n\nSo, to track all pointer movements, including when it goes above/below the `thumb`, we had to assign `pointermove` event handler on the whole `document`.\n\nThat solution looks a bit \"dirty\". One of the problems is that pointer movements around the document may cause side effects, trigger other event handlers, totally not related to the slider.\n\nPointer capturing provides a means to bind `pointermove` to `thumb` and avoid any such problems:\n\n- We can call `thumb.setPointerCapture(event.pointerId)` in `pointerdown` handler,\n- Then future pointer events until `pointerup/cancel` will be retargeted to `thumb`. \n- When `pointerup` happens (dragging complete), the binding is removed automatically, we don't need to care about it.\n\nSo, even if the user moves the pointer around the whole document, events handlers will be called on `thumb`. Besides, coordinate properties of the event objects, such as `clientX/clientY` will still be correct - the capturing only affects `target/currentTarget`.\n\nHere's the essential code:\n\n```js\nthumb.onpointerdown = function(event) {\n  // retarget all pointer events (until pointerup) to thumb\n  thumb.setPointerCapture(event.pointerId);\n};\n\nthumb.onpointermove = function(event) {\n  // moving the slider: listen on the thumb, as all pointer events are retargeted to it\n  let newLeft = event.clientX - slider.getBoundingClientRect().left;\n  thumb.style.left = newLeft + 'px';\n};\n\n// note: no need to call thumb.releasePointerCapture, \n// it happens on pointerup automatically\n```\n\n```online\nThe full demo:\n\n[iframe src=\"slider\" height=100 edit]\n```\n\nAt the end, pointer capturing gives us two benefits:\n1. The code becomes cleaner as we don't need to add/remove handlers on the whole `document` any more. The binding is released automatically.\n2. If there are any `pointermove` handlers in the document, they won't be accidentally triggered by the pointer while the user is dragging the slider.\n\n### Pointer capturing events\n\nThere are two associated pointer events:\n\n- `gotpointercapture` fires when an element uses `setPointerCapture` to enable capturing.\n- `lostpointercapture` fires when the capture is released: either explicitly with `releasePointerCapture` call, or automatically on `pointerup`/`pointercancel`.\n\n## Summary\n\nPointer events allow handling mouse, touch and pen events simultaneously, with a single piece of code.\n\nPointer events extend mouse events. We can replace `mouse` with `pointer` in event names and expect our code to continue working for mouse, with better support for other device types.\n\nFor drag'n'drops and complex touch interactions that the browser may decide to hijack and handle on its own - remember to cancel the default action on events and set `touch-events: none` in CSS for elements that we engage.\n\nAdditional abilities of pointer events are:\n\n- Multi-touch support using `pointerId` and `isPrimary`.\n- Device-specific properties, such as `pressure`, `width/height`, and others.\n- Pointer capturing: we can retarget all pointer events to a specific element until `pointerup`/`pointercancel`.\n\nAs of now, pointer events are supported in all major browsers, so we can safely switch to them, especially if IE10- and Safari 12- are not needed. And even with those browsers, there are polyfills that enable the support of pointer events.\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/ball-2.view/index.html",
    "content": "<!doctype html>\n<body style=\"height: 200px\">\n  <style>\n    #ball {\n      touch-action: none;\n    }\n  </style>\n\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n  <script>\n\n    ball.onpointerdown = log;\n    ball.onpointerup = log;\n    ball.onpointermove = log;\n    ball.onpointercancel = log;\n\n    ball.ondragstart = () => false;\n\n    let lastEventType;\n    let n = 1;\n    function log(event) {\n      if (lastEventType == event.type) {\n        n++;\n        text.value = text.value.replace(/.*\\n$/, `${event.type} * ${n}\\n`);\n        return;\n      }\n      lastEventType = event.type;\n      n = 1;\n      text.value += event.type + '\\n';\n      text.scrollTop = 1e9;\n    }\n  </script>\n\n  <textarea id=\"text\" style=\"display:block;width:300px;height:100px\"></textarea>\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/ball.view/index.html",
    "content": "<!doctype html>\n<body style=\"height: 200px\">\n  <p>Drag the ball.</p>\n\n  <img src=\"https://js.cx/clipart/ball.svg\" style=\"cursor:pointer\" width=\"40\" height=\"40\" id=\"ball\">\n\n  <script>\n\n    ball.onpointerdown = log;\n    ball.onpointerup = log;\n    ball.onpointermove = log;\n    ball.onpointercancel = log;\n\n    let lastEventType;\n    let n = 1;\n    function log(event) {\n      if (lastEventType == event.type) {\n        n++;\n        text.value = text.value.replace(/.*\\n$/, `${event.type} * ${n}\\n`);\n        return;\n      }\n      lastEventType = event.type;\n      n = 1;\n      text.value += event.type + '\\n';\n      text.scrollTop = 1e9;\n    }\n  </script>\n\n  <textarea id=\"text\" style=\"display:block;width:300px;height:100px\"></textarea>\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/multitouch.view/index.html",
    "content": "<!doctype html>\n<body>\n  <style>\n    #area {\n      border: 1px solid black; \n      width: 90%; \n      height: 180px; \n      cursor: pointer;\n      overflow: scroll;\n      user-select: none;\n    }\n  </style>\n  \n  <div id=\"area\">\n  Multi-touch here\n  </div>\n  <script>\n    document.onpointerdown = document.onpointerup = log;\n\n    function log(event) {\n      area.insertAdjacentHTML(\"beforeend\", `\n        <div>${event.type} isPrimary=${event.isPrimary} pointerId=${event.pointerId}</div>\n      `)\n     area.scrollTop = 1e9;\n    }\n  </script>\n\n</body>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider.view/index.html",
    "content": "<!DOCTYPE html>\n<link rel=\"stylesheet\" href=\"style.css\">\n\n<div id=\"slider\" class=\"slider\">\n  <div class=\"thumb\"></div>\n</div>\n\n<script>\n  let thumb = slider.querySelector('.thumb');\n  let shiftX;\n\n  thumb.onpointerdown = function(event) {\n    event.preventDefault(); // prevent selection start (browser action)\n\n    shiftX = event.clientX - thumb.getBoundingClientRect().left;\n\n    thumb.setPointerCapture(event.pointerId);\n  };\n\n  thumb.onpointermove = function(event) {\n    let newLeft = event.clientX - shiftX - slider.getBoundingClientRect().left;\n\n    // if the pointer is out of slider => adjust left to be within the bounaries\n    if (newLeft < 0) {\n      newLeft = 0;\n    }\n    let rightEdge = slider.offsetWidth - thumb.offsetWidth;\n    if (newLeft > rightEdge) {\n      newLeft = rightEdge;\n    }\n\n    thumb.style.left = newLeft + 'px';\n  };\n\n  thumb.ondragstart = () => false;\n\n</script>\n"
  },
  {
    "path": "2-ui/3-event-details/6-pointer-events/slider.view/style.css",
    "content": ".slider {\n  border-radius: 5px;\n  background: #E0E0E0;\n  background: linear-gradient(left top, #E0E0E0, #EEEEEE);\n  width: 310px;\n  height: 15px;\n  margin: 5px;\n}\n\n.thumb {\n  width: 10px;\n  height: 25px;\n  border-radius: 3px;\n  position: relative;\n  left: 10px;\n  top: -5px;\n  background: blue;\n  cursor: pointer;\n}\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.md",
    "content": "\nWe should use two handlers: `document.onkeydown` and `document.onkeyup`.\n\nLet's create a set `pressed = new Set()` to keep currently pressed keys.\n\nThe first handler adds to it, while the second one removes from it. Every time on `keydown` we check if we have enough keys pressed, and run the function if it is so.\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<body>\n\n  <p>Press \"Q\" and \"W\" together (can be in any language).</p>\n\n  <script>\n    function runOnKeys(func, ...codes) {\n      let pressed = new Set();\n\n      document.addEventListener('keydown', function(event) {\n        pressed.add(event.code);\n\n        for (let code of codes) { // are all keys in the set?\n          if (!pressed.has(code)) {\n            return;\n          }\n        }\n\n        // yes, they are\n\n        // during the alert, if the visitor releases the keys,\n        // JavaScript does not get the \"keyup\" event\n        // and pressed set will keep assuming that the key is pressed\n        // so, to evade \"sticky\" keys, we reset the status\n        // if the user wants to run the hotkey again - let them press all keys again\n        pressed.clear();\n\n        func();\n      });\n\n      document.addEventListener('keyup', function(event) {\n        pressed.delete(event.code);\n      });\n\n    }\n\n    runOnKeys(\n      () => alert(\"Hello!\"),\n      \"KeyQ\",\n      \"KeyW\"\n    );\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/2-check-sync-keydown/task.md",
    "content": "importance: 5\n\n---\n\n# Extended hotkeys\n\nCreate a function `runOnKeys(func, code1, code2, ... code_n)` that runs `func` on simultaneous pressing of keys with codes `code1`, `code2`, ..., `code_n`.\n\nFor instance, the code below shows `alert` when `\"Q\"` and `\"W\"` are pressed together (in any language, with or without CapsLock)\n\n```js no-beautify\nrunOnKeys(\n  () => alert(\"Hello!\"),\n  \"KeyQ\",\n  \"KeyW\"\n);\n```\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/article.md",
    "content": "# Keyboard: keydown and keyup\n\nBefore we get to keyboard, please note that on modern devices there are other ways to \"input something\". For instance, people use speech recognition (especially on mobile devices) or copy/paste with the mouse.\n\nSo if we want to track any input into an `<input>` field, then keyboard events are not enough. There's another event named `input` to track changes of an `<input>` field, by any means. And it may be a better choice for such task. We'll cover it later in the chapter <info:events-change-input>.\n\nKeyboard events should be used when we want to handle keyboard actions (virtual keyboard also counts). For instance, to react on arrow keys `key:Up` and `key:Down` or hotkeys (including combinations of keys).\n\n\n## Teststand [#keyboard-test-stand]\n\n```offline\nTo better understand keyboard events, you can use the [teststand](sandbox:keyboard-dump).\n```\n\n```online\nTo better understand keyboard events, you can use the teststand below.\n\nTry different key combinations in the text field.\n\n[codetabs src=\"keyboard-dump\" height=480]\n```\n\n\n## Keydown and keyup\n\nThe `keydown` events happens when a key is pressed down, and then `keyup` -- when it's released.\n\n### event.code and event.key\n\nThe `key` property of the event object allows to get the character, while the `code` property of the event object allows to get the \"physical key code\".\n\nFor instance, the same key `key:Z` can be pressed with or without `key:Shift`. That gives us two different characters: lowercase `z` and uppercase `Z`.\n\nThe `event.key` is exactly the character, and it will be different. But `event.code` is the same:\n\n| Key          | `event.key` | `event.code` |\n|--------------|-------------|--------------|\n| `key:Z`      |`z` (lowercase)         |`KeyZ`        |\n| `key:Shift+Z`|`Z` (uppercase)          |`KeyZ`        |\n\n\nIf a user works with different languages, then switching to another language would make a totally different character instead of `\"Z\"`. That will become the value of `event.key`, while `event.code` is always the same: `\"KeyZ\"`.\n\n```smart header=\"\\\"KeyZ\\\" and other key codes\"\nEvery key has the code that depends on its location on the keyboard. Key codes described in the [UI Events code specification](https://www.w3.org/TR/uievents-code/).\n\nFor instance:\n- Letter keys have codes `\"Key<letter>\"`: `\"KeyA\"`, `\"KeyB\"` etc.\n- Digit keys have codes: `\"Digit<number>\"`: `\"Digit0\"`, `\"Digit1\"` etc.\n- Special keys are coded by their names: `\"Enter\"`, `\"Backspace\"`, `\"Tab\"` etc.\n\nThere are several widespread keyboard layouts, and the specification gives key codes for each of them.\n\nRead the [alphanumeric section of the spec](https://www.w3.org/TR/uievents-code/#key-alphanumeric-section) for more codes, or just press a key in the [teststand](#keyboard-test-stand) above.\n```\n\n```warn header=\"Case matters: `\\\"KeyZ\\\"`, not `\\\"keyZ\\\"`\"\nSeems obvious, but people still make mistakes.\n\nPlease evade mistypes: it's `KeyZ`, not `keyZ`. The check like `event.code==\"keyZ\"` won't work: the first letter of `\"Key\"` must be uppercase.\n```\n\nWhat if a key does not give any character? For instance, `key:Shift` or `key:F1` or others. For those keys, `event.key` is approximately the same as `event.code`:\n\n| Key          | `event.key` | `event.code` |\n|--------------|-------------|--------------|\n| `key:F1`      |`F1`          |`F1`        |\n| `key:Backspace`      |`Backspace`          |`Backspace`        |\n| `key:Shift`|`Shift`          |`ShiftRight` or `ShiftLeft`        |\n\nPlease note that `event.code` specifies exactly which key is pressed. For instance, most keyboards have two `key:Shift` keys: on the left and on the right side. The `event.code` tells us exactly which one was pressed, and `event.key` is responsible for the \"meaning\" of the key: what it is (a \"Shift\").\n\nLet's say, we want to handle a hotkey: `key:Ctrl+Z` (or `key:Cmd+Z` for Mac). Most text editors hook the \"Undo\" action on it. We can set a listener on `keydown` and check which key is pressed.\n\nThere's a dilemma here: in such a listener, should we check the value of `event.key` or `event.code`?\n\nOn one hand, the value of `event.key` is a character, it changes depending on the language. If the visitor has several languages in OS and switches between them, the same key gives different characters. So it makes sense to check `event.code`, it's always the same.\n\nLike this:\n\n```js run\ndocument.addEventListener('keydown', function(event) {\n  if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {\n    alert('Undo!')\n  }\n});\n```\n\nOn the other hand, there's a problem with `event.code`. For different keyboard layouts, the same key may have different characters.\n\nFor example, here are US layout (\"QWERTY\") and German layout (\"QWERTZ\") under it (from Wikipedia):\n\n![](us-layout.svg)\n\n![](german-layout.svg)\n\nFor the same key, US layout has \"Z\", while German layout has \"Y\" (letters are swapped).\n\nLiterally, `event.code` will equal `KeyZ` for people with German layout when they press `key:Y`.\n\nIf we check `event.code == 'KeyZ'` in our code, then for people with German layout such test will pass when they press `key:Y`.\n\nThat sounds really odd, but so it is. The [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system) explicitly mentions such behavior.\n\nSo, `event.code` may match a wrong character for unexpected layout. Same letters in different layouts may map to different physical keys, leading to different codes. Luckily, that happens only with several codes, e.g. `keyA`, `keyQ`, `keyZ` (as we've seen), and doesn't happen with special keys such as `Shift`. You can find the list in the [specification](https://www.w3.org/TR/uievents-code/#table-key-code-alphanumeric-writing-system).\n\nTo reliably track layout-dependent characters, `event.key` may be a better way.\n\nOn the other hand, `event.code` has the benefit of staying always the same, bound to the physical key location, even if the visitor changes languages. So hotkeys that rely on it work well even in case of a language switch.\n\nDo we want to handle layout-dependant keys? Then `event.key` is the way to go.\n\nOr we want a hotkey to work even after a language switch? Then `event.code` may be better.\n\n## Auto-repeat\n\nIf a key is being pressed for a long enough time, it starts to \"auto-repeat\": the `keydown` triggers again and again, and then when it's released we finally get `keyup`. So it's kind of normal to have many `keydown` and a single `keyup`.\n\nFor events triggered by auto-repeat, the event object has `event.repeat` property set to `true`.\n\n\n## Default actions\n\nDefault actions vary, as there are many possible things that may be initiated by the keyboard.\n\nFor instance:\n\n- A character appears on the screen (the most obvious outcome).\n- A character is deleted (`key:Delete` key).\n- The page is scrolled (`key:PageDown` key).\n- The browser opens the \"Save Page\" dialog (`key:Ctrl+S`)\n-  ...and so on.\n\nPreventing the default action on `keydown` can cancel most of them, with the exception of OS-based special keys. For instance, on Windows `key:Alt+F4` closes the current browser window. And there's no way to stop it by preventing the default action in JavaScript.\n\nFor instance, the `<input>` below expects a phone number, so it does not accept keys except digits, `+`, `()` or `-`:\n\n```html autorun height=60 run\n<script>\nfunction checkPhoneKey(key) {\n  return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-';\n}\n</script>\n<input *!*onkeydown=\"return checkPhoneKey(event.key)\"*/!* placeholder=\"Phone, please\" type=\"tel\">\n```\n\nPlease note that special keys, such as `key:Backspace`, `key:Left`, `key:Right`, `key:Ctrl+V`, do not work in the input. That's a side-effect of the strict filter `checkPhoneKey`.\n\nLet's relax it a little bit:\n\n\n```html autorun height=60 run\n<script>\nfunction checkPhoneKey(key) {\n  return (key >= '0' && key <= '9') || key == '+' || key == '(' || key == ')' || key == '-' ||\n    key == 'ArrowLeft' || key == 'ArrowRight' || key == 'Delete' || key == 'Backspace';\n}\n</script>\n<input onkeydown=\"return checkPhoneKey(event.key)\" placeholder=\"Phone, please\" type=\"tel\">\n```\n\nNow arrows and deletion works well.\n\n...But we still can enter anything by using a mouse and right-click + Paste. So the filter is not 100% reliable. We can just let it be like that, because most of time it works. Or an alternative approach would be to track the `input` event -- it triggers after any modification. There we can check the new value and highlight/modify it when it's invalid.\n\n## Legacy\n\nIn the past, there was a `keypress` event, and also `keyCode`, `charCode`, `which` properties of the event object.\n\nThere were so many browser incompatibilities while working with them, that developers of the specification had no way, other than deprecating all of them and creating new, modern events (described above in this chapter). The old code still works, as browsers keep supporting them, but there's totally no need to use those any more.\n\n## Mobile Keyboards\n\nWhen using virtual/mobile keyboards, formally known as IME (Input-Method Editor), the W3C standard states that a KeyboardEvent's [`e.keyCode` should be `229`](https://www.w3.org/TR/uievents/#determine-keydown-keyup-keyCode) and [`e.key` should be `\"Unidentified\"`](https://www.w3.org/TR/uievents-key/#key-attr-values).\n\nWhile some of these keyboards might still use the right values for `e.key`, `e.code`, `e.keyCode`... when pressing certain keys such as arrows or backspace, there's no guarantee, so your keyboard logic might not always work on mobile devices.\n\n## Summary\n\nPressing a key always generates a keyboard event, be it symbol keys or special keys like `key:Shift` or `key:Ctrl` and so on. The only exception is `key:Fn` key that sometimes presents on a laptop keyboard. There's no keyboard event for it, because it's often implemented on lower level than OS.\n\nKeyboard events:\n\n- `keydown` -- on pressing the key (auto-repeats if the key is pressed for long),\n- `keyup` -- on releasing the key.\n\nMain keyboard event properties:\n\n- `code` -- the \"key code\" (`\"KeyA\"`, `\"ArrowLeft\"` and so on), specific to the physical location of the key on keyboard.\n- `key` -- the character (`\"A\"`, `\"a\"` and so on), for non-character keys, such as `key:Esc`, usually has the same value  as `code`.\n\nIn the past, keyboard events were sometimes used to track user input in form fields. That's not reliable, because the input can come from various sources. We have `input` and `change` events to handle any input (covered later in the chapter <info:events-change-input>). They trigger after any kind of input, including copy-pasting or speech recognition.\n\nWe should use keyboard events when we really want keyboard. For example, to react on hotkeys or special keys.\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <form id=\"form\" onsubmit=\"return false\">\n\n    Prevent default for:\n    <label>\n      <input type=\"checkbox\" name=\"keydownStop\" value=\"1\"> keydown</label>&nbsp;&nbsp;&nbsp;\n    <label>\n      <input type=\"checkbox\" name=\"keyupStop\" value=\"1\"> keyup</label>\n\n    <p>\n      Ignore:\n      <label>\n        <input type=\"checkbox\" name=\"keydownIgnore\" value=\"1\"> keydown</label>&nbsp;&nbsp;&nbsp;\n      <label>\n        <input type=\"checkbox\" name=\"keyupIgnore\" value=\"1\"> keyup</label>\n    </p>\n\n    <p>Focus on the input field and press a key.</p>\n\n    <input type=\"text\" placeholder=\"Press keys here\" id=\"kinput\">\n\n    <textarea id=\"area\"></textarea>\n    <input type=\"button\" value=\"Clear\" onclick=\"area.value = ''\" />\n  </form>\n  <script src=\"script.js\"></script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/script.js",
    "content": "kinput.onkeydown = kinput.onkeyup = kinput.onkeypress = handle;\n\nlet lastTime = Date.now();\n\nfunction handle(e) {\n  if (form.elements[e.type + 'Ignore'].checked) return;\n\n  let text = e.type +\n    ' key=' + e.key +\n    ' code=' + e.code +\n    (e.shiftKey ? ' shiftKey' : '') +\n    (e.ctrlKey ? ' ctrlKey' : '') +\n    (e.altKey ? ' altKey' : '') +\n    (e.metaKey ? ' metaKey' : '') +\n    (e.repeat ? ' (repeat)' : '') +\n    \"\\n\";\n\n  if (area.value && Date.now() - lastTime > 250) {\n    area.value += new Array(81).join('-') + '\\n';\n  }\n  lastTime = Date.now();\n\n  area.value += text;\n\n  if (form.elements[e.type + 'Stop'].checked) {\n    e.preventDefault();\n  }\n}\n"
  },
  {
    "path": "2-ui/3-event-details/7-keyboard-events/keyboard-dump.view/style.css",
    "content": "#kinput {\n  font-size: 150%;\n  box-sizing: border-box;\n  width: 95%;\n}\n\n#area {\n  width: 95%;\n  box-sizing: border-box;\n  height: 250px;\n  border: 1px solid black;\n  display: block;\n}\n\nform label {\n  display: inline;\n  white-space: nowrap;\n}"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/solution.md",
    "content": "The core of the solution is a function that adds more dates to the page (or loads more stuff in real-life) while we're at the page end.\n\nWe can call it immediately and add as a `window.onscroll` handler.\n\nThe most important question is: \"How do we detect that the page is scrolled to bottom?\"\n\nLet's use window-relative coordinates.\n\nThe document is represented (and contained) within `<html>` tag, that is `document.documentElement`.\n\nWe can get window-relative coordinates of the whole document as `document.documentElement.getBoundingClientRect()`, the `bottom` property will be window-relative coordinate of the document bottom.\n\nFor instance, if the height of the whole HTML document is `2000px`, then:\n\n```js\n// when we're on the top of the page\n// window-relative top = 0\ndocument.documentElement.getBoundingClientRect().top = 0\n\n// window-relative bottom = 2000\n// the document is long, so that is probably far beyond the window bottom\ndocument.documentElement.getBoundingClientRect().bottom = 2000\n```\n\nIf we scroll `500px` below, then:\n\n```js\n// document top is above the window 500px\ndocument.documentElement.getBoundingClientRect().top = -500\n// document bottom is 500px closer\ndocument.documentElement.getBoundingClientRect().bottom = 1500\n```\n\nWhen we scroll till the end, assuming that the window height is `600px`:\n\n\n```js\n// document top is above the window 1400px\ndocument.documentElement.getBoundingClientRect().top = -1400\n// document bottom is below the window 600px\ndocument.documentElement.getBoundingClientRect().bottom = 600\n```\n\nPlease note that the `bottom` can't be `0`, because it never reaches the window top. The lowest limit of the `bottom` coordinate is the window height (we assumed it to be `600`), we can't scroll it any more up.\n\nWe can obtain the window height as `document.documentElement.clientHeight`.\n\nFor our task, we need to know when the document bottom is not no more than `100px` away from it (that is: `600-700px`, if the height is `600`).\n\nSo here's the function:\n\n```js\nfunction populate() {\n  while(true) {\n    // document bottom\n    let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;\n\n    // if the user hasn't scrolled far enough (>100px to the end)\n    if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;\n    \n    // let's add more data\n    document.body.insertAdjacentHTML(\"beforeend\", `<p>Date: ${new Date()}</p>`);\n  }\n}\n```\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n<h1>Scroll me</h1>\n\n<script>\n  function populate() {\n    while(true) {\n      let windowRelativeBottom = document.documentElement.getBoundingClientRect().bottom;\n      if (windowRelativeBottom > document.documentElement.clientHeight + 100) break;\n      document.body.insertAdjacentHTML(\"beforeend\", `<p>Date: ${new Date()}</p>`);\n    }\n  }\n\n  window.addEventListener('scroll', populate);\n\n  populate(); // init document\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n<h1>Scroll me</h1>\n\n<script>\n  // ... your code ...\n</script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/1-endless-page/task.md",
    "content": "importance: 5\n\n---\n\n# Endless page\n\nCreate an endless page. When a visitor scrolls it to the end, it auto-appends current date-time to the text (so that a visitor can scroll more).\n\nLike this:\n\n[iframe src=\"solution\" height=200]\n\nPlease note two important features of the scroll:\n\n1. **The scroll is \"elastic\".** We can scroll a little beyond the document start or end in some browsers/devices (empty space below is shown, and then the document will automatically \"bounces back\" to normal).\n2. **The scroll is imprecise.** When we scroll to page end, then we may be in fact like 0-50px away from the real document bottom.\n\nSo, \"scrolling to the end\" should mean that the visitor is no more than 100px away from the document end.\n\nP.S. In real life we may want to show \"more messages\" or \"more goods\".\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    body,\n    html {\n      height: 100%;\n      width: 100%;\n      padding: 0;\n      margin: 0;\n    }\n\n    #matrix {\n      width: 400px;\n      margin: auto;\n      overflow: auto;\n      text-align: justify;\n    }\n\n    #arrowTop {\n      height: 9px;\n      width: 14px;\n      color: green;\n      position: fixed;\n      top: 10px;\n      left: 10px;\n      cursor: pointer;\n    }\n\n    #arrowTop::before {\n      content: '▲';\n    }\n\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"matrix\">\n    <script>\n      for (let i = 0; i < 2000; i++) document.writeln(i)\n    </script>\n  </div>\n\n  <div id=\"arrowTop\" hidden></div>\n\n  <script>\n\n    arrowTop.onclick = function() {\n      window.scrollTo(pageXOffset, 0);\n      // after scrollTo, there will be a \"scroll\" event, so the arrow will hide automatically\n    };\n\n    window.addEventListener('scroll', function() {\n      arrowTop.hidden = (pageYOffset < document.documentElement.clientHeight);\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <style>\n    body,\n    html {\n      height: 100%;\n      width: 100%;\n      padding: 0;\n      margin: 0;\n    }\n\n    #matrix {\n      width: 400px;\n      margin: auto;\n      overflow: auto;\n      text-align: justify;\n    }\n\n    #arrowTop {\n      height: 9px;\n      width: 14px;\n      color: green;\n      position: fixed;\n      top: 10px;\n      left: 10px;\n      cursor: pointer;\n    }\n\n    #arrowTop::before {\n      content: '▲';\n    }\n\n  </style>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <div id=\"matrix\">\n    <script>\n      for (let i = 0; i < 2000; i++) document.writeln(i)\n    </script>\n  </div>\n\n  <div id=\"arrowTop\"></div>\n\n  <script>\n    // ... your code ...\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/2-updown-button/task.md",
    "content": "importance: 5\n\n---\n\n# Up/down button\n\nCreate a \"to the top\" button to help with page scrolling.\n\nIt should work like this:\n- While the page is not scrolled down at least for the window height -- it's invisible.\n- When the page is scrolled down more than the window height -- there appears an \"upwards\" arrow in the left-top corner. If the page is scrolled back, it disappears.\n- When the arrow is clicked, the page scrolls to the top.\n\nLike this (top-left corner, scroll to see):\n\n[iframe border=\"1\" height=\"200\" link src=\"solution\"]\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.md",
    "content": "The `onscroll` handler should check which images are visible and show them.\n\nWe also want to run it when the page loads, to detect immediately visible images and load them.\n\nThe code should execute when the document is loaded, so that it has access to its content.\n\nOr put it at the `<body>` bottom:\n\n```js\n// ...the page content is above...\n\nfunction isVisible(elem) {\n\n  let coords = elem.getBoundingClientRect();\n\n  let windowHeight = document.documentElement.clientHeight;\n\n  // top elem edge is visible?\n  let topVisible = coords.top > 0 && coords.top < windowHeight;\n\n  // bottom elem edge is visible?\n  let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;\n\n  return topVisible || bottomVisible;\n}\n```\n\nThe `showVisible()` function uses the visibility check, implemented by `isVisible()`, to load visible images:\n\n```js\nfunction showVisible() {\n  for (let img of document.querySelectorAll('img')) {\n    let realSrc = img.dataset.src;\n    if (!realSrc) continue;\n\n    if (isVisible(img)) {\n      img.src = realSrc;\n      img.dataset.src = '';\n    }\n  }\n}\n\n*!*\nshowVisible();\nwindow.onscroll = showVisible;\n*/!*\n```\n\nP.S. The solution also has a variant of `isVisible` that \"preloads\" images that are within 1 page above/below the current document scroll.\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p>Text and pictures are from https://wikipedia.org.</p>\n\n  <h3>All images with <code>data-src</code> load when become visible.</h3>\n\n  <h1>Solar system</h1>\n\n  <p>The Solar System is the gravitationally bound system comprising the Sun and the objects that\n    orbit it, either directly or indirectly. Of those objects that orbit the Sun directly,\n    the largest eight are the planets, with the remainder being significantly smaller objects,\n    such as dwarf planets and small Solar System bodies.\n    Of the objects that orbit the Sun indirectly, the moons,\n    two are larger than the smallest planet, Mercury.</p>\n\n  <p>The Solar System formed 4.6 billion years ago from the gravitational collapse of a giant\n    interstellar molecular cloud. The vast majority of the system's mass is in the Sun, with most\n    of the remaining mass contained in Jupiter. The four smaller inner planets, Mercury, Venus,\n    Earth and Mars, are terrestrial planets, being primarily composed of rock and metal.\n    The four outer planets are giant planets, being substantially more massive than the terrestrials.\n    The two largest, Jupiter and Saturn, are gas giants, being composed mainly of hydrogen and helium;\n    the two outermost planets, Uranus and Neptune, are ice giants,\n    being composed mostly of substances with relatively high melting points compared with hydrogen\n    and helium, called volatiles, such as water, ammonia and methane.\n    All planets have almost circular orbits that lie within a nearly flat disc called the ecliptic.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/planets.jpg\" width=\"640\" height=\"360\">\n  </figure>\n\n  <h1>Sun</h1>\n\n  <p>The Sun is the Solar System's star and by far its most massive component.\n    Its large mass (332,900 Earth masses) produces temperatures and densities in its core\n    high enough to sustain nuclear fusion of hydrogen into helium, making it a main-sequence star.\n     This releases an enormous amount of energy,\n    mostly radiated into space as electromagnetic radiation peaking in visible light.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/sun.jpg\" width=\"658\" height=\"658\">\n  </figure>\n\n  <h1>Mercury</h1>\n\n  <p>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet\n    in the Solar System (0.055 Earth masses).\n    Mercury has no natural satellites; besides impact craters, its only known geological features\n    are lobed ridges or rupes that were probably produced by a period of contraction early in\n    its history.[67] Mercury's very tenuous atmosphere consists of atoms blasted off its\n    surface by the solar wind.[68] Its relatively large iron core and thin mantle have not yet\n    been adequately explained. Hypotheses include that its outer layers were stripped off by a\n    giant impact; or, that it was prevented from fully accreting by the young Sun's energy.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mercury.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Venus</h1>\n\n  <p>Venus (0.7 AU from the Sun) is close in size to Earth (0.815 Earth masses) and, like Earth,\n   has a thick silicate mantle around an iron core, a substantial atmosphere, and evidence of\n   internal geological activity. It is much drier than Earth, and its atmosphere is ninety times\n    as dense. Venus has no natural satellites. It is the hottest planet, with surface temperatures\n     over 400 °C (752°F), most likely due to the amount of greenhouse gases in the atmosphere.\n   No definitive evidence of current geological activity has been detected on Venus,\n   but it has no magnetic field that would prevent depletion of its substantial atmosphere,\n    which suggests that its atmosphere is being replenished by volcanic eruptions.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/venus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Earth</h1>\n\n  <p>Earth (1 AU from the Sun) is the largest and densest of the inner planets,\n    the only one known to have current geological activity, and the only place where life\n    is known to exist. Its liquid hydrosphere is unique among the terrestrial planets,\n    and it is the only planet where plate tectonics has been observed.\n    Earth's atmosphere is radically different from those of the other planets,\n    having been altered by the presence of life to contain 21% free oxygen.\n    It has one natural satellite, the Moon, the only large satellite of a terrestrial planet\n    in the Solar System.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/earth.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Mars</h1>\n\n  <p>Mars (1.5 AU from the Sun) is smaller than Earth and Venus (0.107 Earth masses).\n    It has an atmosphere of mostly carbon dioxide with a surface pressure of 6.1 millibars\n    (roughly 0.6% of that of Earth). Its surface, peppered with vast volcanoes,\n    such as Olympus Mons, and rift valleys, such as Valles Marineris, shows geological\n    activity that may have persisted until as recently as 2 million years ago.\n     Its red colour comes from iron oxide (rust) in its soil.\n      Mars has two tiny natural satellites (Deimos and Phobos) thought to be captured asteroids.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mars.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Jupiter</h1>\n\n  <p>Jupiter (5.2 AU), at 318 Earth masses, is 2.5 times the mass of all the other planets put together.\n     It is composed largely of hydrogen and helium.\n     Jupiter's strong internal heat creates semi-permanent features in its atmosphere,\n     such as cloud bands and the Great Red Spot. Jupiter has 67 known satellites.\n     The four largest, Ganymede, Callisto, Io, and Europa, show similarities to the terrestrial planets,\n      such as volcanism and internal heating.\n    Ganymede, the largest satellite in the Solar System, is larger than Mercury.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/jupiter.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Saturn</h1>\n  <p>Saturn (9.5 AU), distinguished by its extensive ring system,\n    has several similarities to Jupiter, such as its atmospheric composition and magnetosphere.\n    Although Saturn has 60% of Jupiter's volume, it is less than a third as massive,\n    at 95 Earth masses. Saturn is the only planet of the Solar System that is less dense than water.\n    The rings of Saturn are made up of small ice and rock particles.\n    Saturn has 62 confirmed satellites composed largely of ice.\n    Two of these, Titan and Enceladus, show signs of geological activity.\n    Titan, the second-largest moon in the Solar System, is larger than Mercury\n    and the only satellite in the Solar System with a substantial atmosphere.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/saturn.jpg\" width=\"805\" height=\"390\">\n  </figure>\n\n  <h1>Uranus</h1>\n  <p>Uranus (19.2 AU), at 14 Earth masses, is the lightest of the outer planets.\n    Uniquely among the planets, it orbits the Sun on its side;\n    its axial tilt is over ninety degrees to the ecliptic.\n    It has a much colder core than the other giant planets and radiates very little heat into space.\n    Uranus has 27 known satellites, the largest ones being Titania,\n    Oberon, Umbriel, Ariel, and Miranda.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/uranus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Neptune</h1>\n  <p>Neptune (30.1 AU), though slightly smaller than Uranus, is more massive (equivalent to 17 Earths)\n     and hence more dense. It radiates more internal heat,\n     but not as much as Jupiter or Saturn.\n     Neptune has 14 known satellites. The largest, Triton, is geologically active,\n     with geysers of liquid nitrogen.\n     Triton is the only large satellite with a retrograde orbit.\n     Neptune is accompanied in its orbit by several minor planets, termed Neptune trojans,\n     that are in 1:1 resonance with it.</p>\n\n   <figure>\n     <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/neptune.jpg\" width=\"390\" height=\"390\">\n   </figure>\n\n   <script>\n    /**\n     * Tests if the element is visible (within the visible part of the page)\n     * It's enough that the top or bottom edge of the element are visible\n     */\n    function isVisible(elem) {\n\n      let coords = elem.getBoundingClientRect();\n\n      let windowHeight = document.documentElement.clientHeight;\n\n      // top elem edge is visible OR bottom elem edge is visible\n      let topVisible = coords.top > 0 && coords.top < windowHeight;\n      let bottomVisible = coords.bottom < windowHeight && coords.bottom > 0;\n\n      return topVisible || bottomVisible;\n    }\n\n    /**\n    A variant of the test that considers the element visible if it's no more than\n    one page after/behind the current screen.\n\n    function isVisible(elem) {\n\n      let coords = elem.getBoundingClientRect();\n\n      let windowHeight = document.documentElement.clientHeight;\n\n      let extendedTop = -windowHeight;\n      let extendedBottom = 2 * windowHeight;\n\n      // top visible || bottom visible\n      let topVisible = coords.top > extendedTop && coords.top < extendedBottom;\n      let bottomVisible = coords.bottom < extendedBottom && coords.bottom > extendedTop;\n\n      return topVisible || bottomVisible;\n    }\n    */\n\n    function showVisible() {\n      for (let img of document.querySelectorAll('img')) {\n        let realSrc = img.dataset.src;\n        if (!realSrc) continue;\n\n        if (isVisible(img)) {\n          // disable caching\n          // this line should be removed in production code\n          realSrc += '?nocache=' + Math.random();\n\n          img.src = realSrc;\n\n          img.dataset.src = '';\n        }\n      }\n\n    }\n\n    window.addEventListener('scroll', showVisible);\n    showVisible();\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <p>Text and pictures are from https://wikipedia.org.</p>\n\n  <h3>All images with <code>data-src</code> load when become visible.</h3>\n\n  <h1>Solar system</h1>\n\n  <p>The Solar System is the gravitationally bound system comprising the Sun and the objects that\n    orbit it, either directly or indirectly. Of those objects that orbit the Sun directly,\n    the largest eight are the planets, with the remainder being significantly smaller objects,\n    such as dwarf planets and small Solar System bodies.\n    Of the objects that orbit the Sun indirectly, the moons,\n    two are larger than the smallest planet, Mercury.</p>\n\n  <p>The Solar System formed 4.6 billion years ago from the gravitational collapse of a giant\n    interstellar molecular cloud. The vast majority of the system's mass is in the Sun, with most\n    of the remaining mass contained in Jupiter. The four smaller inner planets, Mercury, Venus,\n    Earth and Mars, are terrestrial planets, being primarily composed of rock and metal.\n    The four outer planets are giant planets, being substantially more massive than the terrestrials.\n    The two largest, Jupiter and Saturn, are gas giants, being composed mainly of hydrogen and helium;\n    the two outermost planets, Uranus and Neptune, are ice giants,\n    being composed mostly of substances with relatively high melting points compared with hydrogen\n    and helium, called volatiles, such as water, ammonia and methane.\n    All planets have almost circular orbits that lie within a nearly flat disc called the ecliptic.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/planets.jpg\" width=\"640\" height=\"360\">\n  </figure>\n\n  <h1>Sun</h1>\n\n  <p>The Sun is the Solar System's star and by far its most massive component.\n    Its large mass (332,900 Earth masses) produces temperatures and densities in its core\n    high enough to sustain nuclear fusion of hydrogen into helium, making it a main-sequence star.\n     This releases an enormous amount of energy,\n    mostly radiated into space as electromagnetic radiation peaking in visible light.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/sun.jpg\" width=\"658\" height=\"658\">\n  </figure>\n\n  <h1>Mercury</h1>\n\n  <p>Mercury (0.4 AU from the Sun) is the closest planet to the Sun and the smallest planet\n    in the Solar System (0.055 Earth masses).\n    Mercury has no natural satellites; besides impact craters, its only known geological features\n    are lobed ridges or rupes that were probably produced by a period of contraction early in\n    its history.[67] Mercury's very tenuous atmosphere consists of atoms blasted off its\n    surface by the solar wind.[68] Its relatively large iron core and thin mantle have not yet\n    been adequately explained. Hypotheses include that its outer layers were stripped off by a\n    giant impact; or, that it was prevented from fully accreting by the young Sun's energy.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mercury.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Venus</h1>\n\n  <p>Venus (0.7 AU from the Sun) is close in size to Earth (0.815 Earth masses) and, like Earth,\n   has a thick silicate mantle around an iron core, a substantial atmosphere, and evidence of\n   internal geological activity. It is much drier than Earth, and its atmosphere is ninety times\n    as dense. Venus has no natural satellites. It is the hottest planet, with surface temperatures\n     over 400 °C (752°F), most likely due to the amount of greenhouse gases in the atmosphere.\n   No definitive evidence of current geological activity has been detected on Venus,\n   but it has no magnetic field that would prevent depletion of its substantial atmosphere,\n    which suggests that its atmosphere is being replenished by volcanic eruptions.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/venus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Earth</h1>\n\n  <p>Earth (1 AU from the Sun) is the largest and densest of the inner planets,\n    the only one known to have current geological activity, and the only place where life\n    is known to exist. Its liquid hydrosphere is unique among the terrestrial planets,\n    and it is the only planet where plate tectonics has been observed.\n    Earth's atmosphere is radically different from those of the other planets,\n    having been altered by the presence of life to contain 21% free oxygen.\n    It has one natural satellite, the Moon, the only large satellite of a terrestrial planet\n    in the Solar System.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/earth.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Mars</h1>\n\n  <p>Mars (1.5 AU from the Sun) is smaller than Earth and Venus (0.107 Earth masses).\n    It has an atmosphere of mostly carbon dioxide with a surface pressure of 6.1 millibars\n    (roughly 0.6% of that of Earth). Its surface, peppered with vast volcanoes,\n    such as Olympus Mons, and rift valleys, such as Valles Marineris, shows geological\n    activity that may have persisted until as recently as 2 million years ago.\n     Its red colour comes from iron oxide (rust) in its soil.\n      Mars has two tiny natural satellites (Deimos and Phobos) thought to be captured asteroids.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/mars.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Jupiter</h1>\n\n  <p>Jupiter (5.2 AU), at 318 Earth masses, is 2.5 times the mass of all the other planets put together.\n     It is composed largely of hydrogen and helium.\n     Jupiter's strong internal heat creates semi-permanent features in its atmosphere,\n     such as cloud bands and the Great Red Spot. Jupiter has 67 known satellites.\n     The four largest, Ganymede, Callisto, Io, and Europa, show similarities to the terrestrial planets,\n      such as volcanism and internal heating.\n    Ganymede, the largest satellite in the Solar System, is larger than Mercury.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/jupiter.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Saturn</h1>\n  <p>Saturn (9.5 AU), distinguished by its extensive ring system,\n    has several similarities to Jupiter, such as its atmospheric composition and magnetosphere.\n    Although Saturn has 60% of Jupiter's volume, it is less than a third as massive,\n    at 95 Earth masses. Saturn is the only planet of the Solar System that is less dense than water.\n    The rings of Saturn are made up of small ice and rock particles.\n    Saturn has 62 confirmed satellites composed largely of ice.\n    Two of these, Titan and Enceladus, show signs of geological activity.\n    Titan, the second-largest moon in the Solar System, is larger than Mercury\n    and the only satellite in the Solar System with a substantial atmosphere.</p>\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/saturn.jpg\" width=\"805\" height=\"390\">\n  </figure>\n\n  <h1>Uranus</h1>\n  <p>Uranus (19.2 AU), at 14 Earth masses, is the lightest of the outer planets.\n    Uniquely among the planets, it orbits the Sun on its side;\n    its axial tilt is over ninety degrees to the ecliptic.\n    It has a much colder core than the other giant planets and radiates very little heat into space.\n    Uranus has 27 known satellites, the largest ones being Titania,\n    Oberon, Umbriel, Ariel, and Miranda.</p>\n\n\n  <figure>\n    <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/uranus.jpg\" width=\"390\" height=\"390\">\n  </figure>\n\n  <h1>Neptune</h1>\n  <p>Neptune (30.1 AU), though slightly smaller than Uranus, is more massive (equivalent to 17 Earths)\n     and hence more dense. It radiates more internal heat,\n     but not as much as Jupiter or Saturn.\n     Neptune has 14 known satellites. The largest, Triton, is geologically active,\n     with geysers of liquid nitrogen.\n     Triton is the only large satellite with a retrograde orbit.\n     Neptune is accompanied in its orbit by several minor planets, termed Neptune trojans,\n     that are in 1:1 resonance with it.</p>\n\n   <figure>\n     <img src=\"placeholder.svg\" data-src=\"https://en.js.cx/clipart/solar/neptune.jpg\" width=\"390\" height=\"390\">\n   </figure>\n\n   <script>\n    /**\n     * Tests if the element is visible (within the visible part of the page)\n     * It's enough that the top or bottom edge of the element are visible\n     */\n    function isVisible(elem) {\n      // todo: your code\n    }\n\n    function showVisible() {\n      for (let img of document.querySelectorAll('img')) {\n        let realSrc = img.dataset.src;\n        if (!realSrc) continue;\n\n        if (isVisible(img)) {\n          // disable caching\n          // this line should be removed in production code\n          realSrc += '?nocache=' + Math.random();\n\n          img.src = realSrc;\n\n          img.dataset.src = '';\n        }\n      }\n\n    }\n\n    window.addEventListener('scroll', showVisible);\n    showVisible();\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/3-load-visible-img/task.md",
    "content": "importance: 4\n\n---\n\n# Load visible images\n\nLet's say we have a slow-speed client and want to save their mobile traffic.\n\nFor that purpose we decide not to show images immediately, but rather replace them with placeholders, like this:\n\n```html\n<img *!*src=\"placeholder.svg\"*/!* width=\"128\" height=\"128\" *!*data-src=\"real.jpg\"*/!*>\n```\n\nSo, initially all images are `placeholder.svg`. When the page scrolls to the position where the user can see the image -- we change `src` to the one in `data-src`, and so the image loads.\n\nHere's an example in `iframe`:\n\n[iframe src=\"solution\"]\n\nScroll it to see images load \"on-demand\".\n\nRequirements:\n- When the page loads, those images that are on-screen should load immediately, prior to any scrolling.\n- Some images may be regular, without `data-src`. The code should not touch them.\n- Once an image is loaded, it should not reload any more when scrolled in/out.\n\nP.S. If you can, make a more advanced solution that would \"preload\" images that are one page below/after the current position.\n\nP.P.S. Only vertical scroll is to be handled, no horizontal scrolling.\n"
  },
  {
    "path": "2-ui/3-event-details/8-onscroll/article.md",
    "content": "# Scrolling\n\nThe `scroll` event allows reacting to a page or element scrolling. There are quite a few good things we can do here.\n\nFor instance:\n- Show/hide additional controls or information depending on where in the document the user is.\n- Load more data when the user scrolls down till the end of the page.\n\nHere's a small function to show the current scroll:\n\n```js autorun\nwindow.addEventListener('scroll', function() {\n  document.getElementById('showScroll').innerHTML = window.pageYOffset + 'px';\n});\n```\n\n```online\nIn action:\n\nCurrent scroll = <b id=\"showScroll\">scroll the window</b>\n```\n\nThe `scroll` event works both on the `window` and on scrollable elements.\n\n## Prevent scrolling\n\nHow do we make something unscrollable?\n\nWe can't prevent scrolling by using `event.preventDefault()` in `onscroll` listener, because it triggers *after* the scroll has already happened.\n\nBut we can prevent scrolling by `event.preventDefault()` on an event that causes the scroll, for instance `keydown` event for `key:pageUp` and `key:pageDown`.\n\nIf we add an event handler to these events and `event.preventDefault()` in it, then the scroll won't start.\n\nThere are many ways to initiate a scroll, so it's more reliable to use CSS, `overflow` property.\n\nHere are few tasks that you can solve or look through to see applications of `onscroll`.\n"
  },
  {
    "path": "2-ui/3-event-details/index.md",
    "content": "# UI Events\n\nHere we cover most important user interface events and how to work with them.\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/1-add-select-option/solution.md",
    "content": "The solution, step by step:\n\n```html run\n<select id=\"genres\">\n  <option value=\"rock\">Rock</option>\n  <option value=\"blues\" selected>Blues</option>\n</select>\n\n<script>\n  // 1)\n  let selectedOption = genres.options[genres.selectedIndex];\n  alert( selectedOption.value );\n\n  // 2)\n  let newOption = new Option(\"Classic\", \"classic\");\n  genres.append(newOption);\n\n  // 3)\n  newOption.selected = true;\n</script>\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/1-add-select-option/task.md",
    "content": "importance: 5\n\n---\n\n# Tambahkan option pada select\n\nThere's a `<select>`:\n\n```html\n<select id=\"genres\">\n  <option value=\"rock\">Rock</option>\n  <option value=\"blues\" selected>Blues</option>\n</select>\n```\n\nGunakan Javascript untuk:\n\n1. Tampilkan nilai dan teks dari selected option.\n2. Tambahkan sebuah option: `<option value=\"classic\">Classic</option>`.\n3. Buat menjadi selected.\n\nNote, jika semua benar, alert akan menampilkan `blues`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/1-form-elements/article.md",
    "content": "# Metode dan properti form\n\nForm dan elemen kontrol, contohnya seperti `<input>` memiliki banyak properti khusus dan event.\n\nBekerja dengan form akan lebih mudah ketika kita mempelajarinya.\n\n## Navigasi: form dan elemen\n\nForm dokumen adalah anggota dari koleksi khusus `document.forms`.\n\nItu disebut _\"named collection\"_: itu keduanya memiliki nama(name) dan terurut(index). Kita bisa menggunakan keduanya baik dengan nama atau nomor(index) pada dokumen untuk mendapatkan form.\n\n```js no-beautify\ndocument.forms.my; // the form with name=\"my\"\ndocument.forms[0]; // the first form in the document\n```\n\nKetika kita mempunyai sebuah form, maka elemen apapun tersedia di dalam _named collection/koleksi nama_ `form.elements`.\n\nMisalnya:\n\n```html run height=40\n<form name=\"my\">\n  <input name=\"one\" value=\"1\" />\n  <input name=\"two\" value=\"2\" />\n</form>\n\n<script>\n  // dapatkan form\n  let form = document.forms.my; // <form name=\"my\"> elemen\n\n  // dapatkan element\n  let elem = form.elements.one; // <input name=\"one\"> elemen\n\n  alert(elem.value); // 1\n</script>\n```\n\nAda suatu saat dimana ada beberapa elemen dengan nama yang sama, hal itu sering terjadi dengan _radio buttons_.\n\nDalam hal tersebut `form.elements[name]`adalah sebuah _collection/koleksi_, misalnya:\n\n```html run height=40\n<form>\n  <input type=\"radio\" *!*name=\"age\"*/!* value=\"10\">\n  <input type=\"radio\" *!*name=\"age\"*/!* value=\"20\">\n</form>\n\n<script>\nlet form = document.forms[0];\n\nlet ageElems = form.elements.age;\n\n*!*\nalert(ageElems[0]); // [object HTMLInputElement]\n*/!*\n</script>\n```\n\nNavigasi properti ini tidak bergantung pada struktur tag. Semua elemen kontrol, tak peduli seberapa dalam mereka didalam form, mereka tersedia di dalam `form.elements`.\n\n````smart header=\"Fieldsets sebagai \"subforms\"\"\nSebuah form mungkin punya satu atau banyak elemen `<fieldset>` didalamnya. Mereka juga punya `elements` properti yang mencatumkan form kontrol didalamnya.\n\nMisalnya:\n\n```html run height=80\n<body>\n  <form id=\"form\">\n    <fieldset name=\"userFields\">\n      <legend>info</legend>\n      <input name=\"login\" type=\"text\">\n    </fieldset>\n  </form>\n\n  <script>\n    alert(form.elements.login); // <input name=\"login\">\n\n*!*\n    let fieldset = form.elements.userFields;\n    alert(fieldset); // HTMLFieldSetElement\n\n     // kita bisa mendapatkan input dengan nama baik dari form maupun dari fieldset\n    alert(fieldset.elements.login == form.elements.login); // true\n*/!*\n  </script>\n</body>\n```\n````\n\n````warn header=\"Shorter notation: `form.name`\" Ada notasi yang lebih ringkas: kita bisa akses/mendapatkan elemen dengan `form[index/name]`.\n\nDengan kata lain, daripada menulisnya dengan `form.elements.login` kita bisa menulis `form.login`.\n\nItu juga berkeja, tetapi disana ada sebuah kesalahan kecil: jika kita akses sebuah elemen, dan lalu mengubah `name`, maka elemen tersebut masih tersedia dengan nama lamanya (sama juga dengan nama barunya).\n\nItu akan lebih mudah saat kita lihat pada sebuah contoh:\n\n```html run height=40\n<form id=\"form\">\n  <input name=\"login\" />\n</form>\n\n<script>\n    alert(form.elements.login == form.login); // true, <input> yang sama\n\n    form.login.name = \"username\"; // mengubah nama input\n\n    // form.elements telah mengupdate nama:\n    alert(form.elements.login); // undefined (tidak terdefinisi)\n    alert(form.elements.username); // input\n\n  *!*\n    // form membolehkan kedua nama: yang baru dan yang lama\n    alert(form.username == form.login); // true\n  */!*\n</script>\n```\n\nItu biasanya bukan sebuah masalah, karena kita jarang mengubah nama dari elemen form.\n\n````\n\n## Backreference: element.form\n\nUntuk elemen apapun, form tersedia sebagai `element.form`. Jadi sebuah form mereferensikan semua elemen, dan elemen referensi dari form.\n\n\nIni gambarannya:\n\n![](form-navigation.svg)\n\nMisalnya:\n\n```html run height=40\n<form id=\"form\">\n  <input type=\"text\" name=\"login\">\n</form>\n\n<script>\n*!*\n  // form -> elemen\n  let login = form.login;\n\n  // elemen -> form\n  alert(login.form); // HTMLFormElement\n*/!*\n</script>\n```\n\n## Element Form\n\nMari bicara tentang kontrol form.\n\n### input dan textarea\n\nKita bisa akses nilai mereka dengan `input.value` (string) atau `input.checked` (boolean) untuk checkboxes.\n\nSeperti ini:\n\n```js\ninput.value = \"New value\";\ntextarea.value = \"New text\";\n\ninput.checked = true; // untuk checkbox atau radio button\n```\n\n```warn header=\"Use `textarea.value`, not `textarea.innerHTML`\"\nHarap dicatat, meskipun `<textarea>...</textarea>` menyimpan nilainya sebagai HTML bersarang(nested), kita tidak boleh menggunakan `textarea.innerHTML` untuk mengaksesnya.\n\nitu hanya menyimpan HTML yang mulanya ada di halaman, bukan nilai saat ini.\n```\n\n### select dan option\n\nSebuah elemen `<select>` mempunyai 3 properti penting:\n\n1. `select.options` -- adalah koleksi dari `<option>` sub-element,\n2. `select.value` -- adalah nilai saat ini yang dipilih(selected) `<option>`,\n3. `select.selectedIndex` -- adalah nomor yang saat ini dipilih(selected) `<option>`.\n\nMereka menyediakan 3 cara berbeda untuk mengatur nilai pada `<select>`:\n\n1. Mencari element `<option>` yang bersangkutan dan atur `option.selected` menjadi `true`.\n2. Atur `select.value`ke nilai.\n3. Atur `select.selectedIndex` ke nomor dari option.\n\nCara pertama adalah yang paling jelas, tetapi cara `(2)` dan `(3)` biasanya lebih nyaman.\n\nLihat contoh berikut:\n\n```html run\n<select id=\"select\">\n  <option value=\"apple\">Apple</option>\n  <option value=\"pear\">Pear</option>\n  <option value=\"banana\">Banana</option>\n</select>\n\n<script>\n  // semua 3 baris kode melakukan hal yang sama\n  select.options[2].selected = true;\n  select.selectedIndex = 2;\n  select.value = 'banana';\n  // please note: options start from zero, so index 2 means the 3rd option.\n</script>\n```\n\nTidak seperti kontrol pada umumnya, `<select>` membolehkan memilih banyak opsi sekaligus jika memiliki atribut`multiple`.Fitur itu jarang digunakan. \n\nJika anda harus, maka gunakan cara pertama: tambah/hapus `selected`properti dari `<option>` sub-element.\n\nKita bisa mendapatkan koleksinya sebagai `select.options`, misalnya:\n\n```html run\n<select id=\"select\" *!*multiple*/!*>\n  <option value=\"blues\" selected>Blues</option>\n  <option value=\"rock\" selected>Rock</option>\n  <option value=\"classic\">Classic</option>\n</select>\n\n<script>\n  // mendapatkan semua nilai selected dari multi-select\n  let selected = Array.from(select.options)\n    .filter(option => option.selected)\n    .map(option => option.value);\n\n  alert(selected); // blues,rock\n</script>\n```\n\nPenjelasan lengkap dari elemen`<select>` tersedia di <https://html.spec.whatwg.org/multipage/forms.html#the-select-element>.\n\n### new Option\n\nIni jarang digunakan. Namun masih ada hal yang menarik.\n\nDi dalam [penjelasan](https://html.spec.whatwg.org/multipage/forms.html#the-option-element) disana ada sintak pendek yang bagus untuk membuat elemen `<option>`:\n\n```js\noption = new Option(text, value, defaultSelected, selected);\n```\n\nThis syntax is optional. We can use `document.createElement('option')` and set attributes manually. Still, it may be shorter, so here are the parameters:\n\n- `text` -- adalah teks didalam option,\n- `value` -- adalah nilai option,\n- `defaultSelected` -- jika `true`, maka `selected` HTML-attribute dibuat,\n- `selected` -- jika `true`, maka option nya *selected*.\n\nDisana mungkin sedikit bingung tentang `defaultSelected` dan `selected`. That's simple: `defaultSelected` *set* HTML-attribute, dengan itu kita bisa dapat menggunakan  `option.getAttribute('selected')`. Dan `selected` - baik opsi *selected* atau tidak, itu yang lebih penting. Biasanya kedua nilai baik di *set* ke `true` atau tidak di *set* (sama dengan `false`).\n\nMisalnya:\n\n```js\nlet option = new Option(\"Text\", \"value\");\n// creates <option value=\"value\">Text</option>\n```\n\nElemen yang sama yang terpilih/*selected*:\n\n```js\nlet option = new Option(\"Text\", \"value\", true, true);\n```\n\nElemen *option*  memiliki properti:\n\n`option.selected`\n: apakah option *selected*.\n\n`option.index`\n: Jumlah option diantara yang lain dalam elemen`<select>`.\n\n`option.text`\n: Konten teks option(dilihat oleh pengunjung).\n\n## Referensi\n\n- Spesifikasi: <https://html.spec.whatwg.org/multipage/forms.html>.\n\n## Kesimpulan\n\nNavigasi Form:\n\n`document.forms`\n: Sebuah form tersedia sebagai `document.forms[name/index]`.\n\n`form.elements`\n: Elemen form tersedia sebagai `form.elements[name/index]`, atau kita bisa gunakan `form[name/index]`. `elements` properti juga dapat berkerja dengan `<fieldset>`.\n\n`element.form`\n: Elemen referensi formulirnya dalam `form` properti.\n\nNilai tersedia sebagai `input.value`, `textarea.value`, `select.value` dll, atau`input.checked` untuk *checkboxes* dan *radio buttons*.\n\nUntuk `<select>` kita juga bisa mendapatkan nilainya dengan index `select.selectedIndex` atau lewat koleksi option `select.options`.\n\nIni adalah dasar-dasar untuk mulai bekerja dengan form. Kita akan melihat banyak contoh lebih lanjut di tutorial.\n\nPada bab selanjutnya, kita akan membahas `focus` dan `blur` event yang mungkin terjadi pada elemen manapapun, tapi biasanya ditangani pada form.\n````\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"my.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul>\n    <li>Click the div to edit.</li>\n    <li>Enter or blur saves the result.</li>\n  </ul>\n\n  HTML is allowed.\n\n  <div id=\"view\" class=\"view\">Text</div>\n\n  <script>\n    let area = null;\n    let view = document.getElementById('view');\n\n    view.onclick = function() {\n      editStart();\n    };\n\n    function editStart() {\n      area = document.createElement('textarea');\n      area.className = 'edit';\n      area.value = view.innerHTML;\n\n      area.onkeydown = function(event) {\n        if (event.key == 'Enter') {\n          this.blur();\n        }\n      };\n\n      area.onblur = function() {\n        editEnd();\n      };\n\n      view.replaceWith(area);\n      area.focus();\n    }\n\n    function editEnd() {\n      view.innerHTML = area.value;\n      area.replaceWith(view);\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/solution.view/my.css",
    "content": ".view,\n.edit {\n  height: 150px;\n  width: 400px;\n  font-family: sans-serif;\n  font-size: 14px;\n  display: block;\n}\n\n.view {\n  /* padding + border = 3px */\n  padding: 2px;\n  border: 1px solid black;\n}\n\n.edit {\n  /* replace padding with border (still 3px not to shift the contents) */\n  border: 3px solid blue;\n  padding: 0px;\n}\n\n.edit:focus {\n  /* remove focus border in Safari */\n  outline: none;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <link type=\"text/css\" rel=\"stylesheet\" href=\"my.css\">\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n\n  <ul>\n    <li>Click the div to edit.</li>\n    <li>Enter or blur saves the result.</li>\n  </ul>\n\n  HTML is allowed.\n\n  <div id=\"view\" class=\"view\">Text</div>\n\n  <script>\n    // ...your code...\n    // Note: <textarea> should have class=\"edit\"\n    // my.css has styles to make it the same size as div\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/source.view/my.css",
    "content": ".view,\n.edit {\n  height: 150px;\n  width: 400px;\n  font-family: sans-serif;\n  font-size: 14px;\n  display: block;\n}\n\n.view {\n  /* padding + border = 3px */\n  padding: 2px;\n  border: 1px solid black;\n}\n\n.edit {\n  /* replace padding with border (still 3px not to shift the contents) */\n  border: 3px solid blue;\n  padding: 0px;\n}\n\n.edit:focus {\n  /* remove focus border in Safari */\n  outline: none;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/3-editable-div/task.md",
    "content": "importance: 5\n\n---\n\n# Editable div\n\nBuat sebuah `<div>` yang berubah menjadi`<textarea>` ketika diklik.\n\nTextarea memungkinkan untuk mengedit HTML pada `<div>`.\n\nKetika user menekan `key:Enter` atau hilang fokus, maka `<textarea>` berubah kembali menjadi `<div>`, dan isinya menjadi HTML di dalam `<div>`.\n\n[demo src=\"solution\"]\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.md",
    "content": "1. Saat diklik -- ganti `innerHTML` dari sel `<textarea>` dengan ukuran yang sama dan tanpa border. Bisa menggunakan JavaScript atau CSS untuk mengatur ukuran yang sesuai.\n2. Ubah `textarea.value` ke `td.innerHTML`.\n3. Fokus pada textarea.\n4. Tampilkan tombol OK/CANCEL dibawah sel, tangani klik pada mereka.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/bagua.css",
    "content": "/* common styles for the table, no need to modify these */\n\nth {\n  text-align: center;\n  font-weight: bold;\n}\n\ntd {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: middle;\n  padding: 10px;\n}\n\n.nw {\n  background-color: #999;\n}\n\n.n {\n  background-color: #03f;\n  color: #fff;\n}\n\n.ne {\n  background-color: #ff6;\n}\n\n.w {\n  background-color: #ff0;\n}\n\n.c {\n  background-color: #60c;\n  color: #fff;\n}\n\n.e {\n  background-color: #09f;\n  color: #fff;\n}\n\n.sw {\n  background-color: #963;\n  color: #fff;\n}\n\n.s {\n  background-color: #f60;\n  color: #fff;\n}\n\n.se {\n  background-color: #0c3;\n  color: #fff;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n  <link rel=\"stylesheet\" href=\"bagua.css\">\n  <link rel=\"stylesheet\" href=\"my.css\">\n\n\n  <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/my.css",
    "content": ".edit-td .edit-area {\n  border: none;\n  margin: 0;\n  padding: 0;\n  display: block;\n  \n  /* remove resizing handle in Firefox */\n  resize: none;\n  \n  /* remove outline on focus in Chrome */\n  outline: none;\n  \n  /* remove scrollbar in IE */\n  overflow: auto;\n}\n\n.edit-controls {\n  position: absolute;\n}\n\n.edit-td {\n  position: relative;\n  padding: 0;\n}"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/solution.view/script.js",
    "content": "let table = document.getElementById('bagua-table');\n\nlet editingTd;\n\ntable.onclick = function(event) {\n\n  // 3 possible targets\n  let target = event.target.closest('.edit-cancel,.edit-ok,td');\n\n  if (!table.contains(target)) return;\n\n  if (target.className == 'edit-cancel') {\n    finishTdEdit(editingTd.elem, false);\n  } else if (target.className == 'edit-ok') {\n    finishTdEdit(editingTd.elem, true);\n  } else if (target.nodeName == 'TD') {\n    if (editingTd) return; // already editing\n\n    makeTdEditable(target);\n  }\n\n};\n\nfunction makeTdEditable(td) {\n  editingTd = {\n    elem: td,\n    data: td.innerHTML\n  };\n\n  td.classList.add('edit-td'); // td is in edit state, CSS also styles the area inside\n\n  let textArea = document.createElement('textarea');\n  textArea.style.width = td.clientWidth + 'px';\n  textArea.style.height = td.clientHeight + 'px';\n  textArea.className = 'edit-area';\n\n  textArea.value = td.innerHTML;\n  td.innerHTML = '';\n  td.appendChild(textArea);\n  textArea.focus();\n\n  td.insertAdjacentHTML(\"beforeEnd\",\n    '<div class=\"edit-controls\"><button class=\"edit-ok\">OK</button><button class=\"edit-cancel\">CANCEL</button></div>'\n  );\n}\n\nfunction finishTdEdit(td, isOk) {\n  if (isOk) {\n    td.innerHTML = td.firstChild.value;\n  } else {\n    td.innerHTML = editingTd.data;\n  }\n  td.classList.remove('edit-td'); \n  editingTd = null;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/bagua.css",
    "content": "/* common styles for the table, no need to modify these */\n\nth {\n  text-align: center;\n  font-weight: bold;\n}\n\ntd {\n  width: 150px;\n  white-space: nowrap;\n  text-align: center;\n  vertical-align: middle;\n  padding: 10px;\n}\n\n.nw {\n  background-color: #999;\n}\n\n.n {\n  background-color: #03f;\n  color: #fff;\n}\n\n.ne {\n  background-color: #ff6;\n}\n\n.w {\n  background-color: #ff0;\n}\n\n.c {\n  background-color: #60c;\n  color: #fff;\n}\n\n.e {\n  background-color: #09f;\n  color: #fff;\n}\n\n.sw {\n  background-color: #963;\n  color: #fff;\n}\n\n.s {\n  background-color: #f60;\n  color: #fff;\n}\n\n.se {\n  background-color: #0c3;\n  color: #fff;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n</head>\n\n<body>\n  <link rel=\"stylesheet\" href=\"bagua.css\">\n  <link rel=\"stylesheet\" href=\"my.css\">\n\n\n  <p>Click on a table cell to edit it. Press OK or CANCEL when you finish.</p>\n\n  <table id=\"bagua-table\">\n    <tr>\n      <th colspan=\"3\"><em>Bagua</em> Chart: Direction, Element, Color, Meaning</th>\n    </tr>\n    <tr>\n      <td class=\"nw\"><strong>Northwest</strong>\n        <br>Metal\n        <br>Silver\n        <br>Elders\n      </td>\n      <td class=\"n\"><strong>North</strong>\n        <br>Water\n        <br>Blue\n        <br>Change\n      </td>\n      <td class=\"ne\"><strong>Northeast</strong>\n        <br>Earth\n        <br>Yellow\n        <br>Direction\n      </td>\n    </tr>\n    <tr>\n      <td class=\"w\"><strong>West</strong>\n        <br>Metal\n        <br>Gold\n        <br>Youth\n      </td>\n      <td class=\"c\"><strong>Center</strong>\n        <br>All\n        <br>Purple\n        <br>Harmony\n      </td>\n      <td class=\"e\"><strong>East</strong>\n        <br>Wood\n        <br>Blue\n        <br>Future\n      </td>\n    </tr>\n    <tr>\n      <td class=\"sw\"><strong>Southwest</strong>\n        <br>Earth\n        <br>Brown\n        <br>Tranquility\n      </td>\n      <td class=\"s\"><strong>South</strong>\n        <br>Fire\n        <br>Orange\n        <br>Fame\n      </td>\n      <td class=\"se\"><strong>Southeast</strong>\n        <br>Wood\n        <br>Green\n        <br>Romance\n      </td>\n    </tr>\n\n  </table>\n\n\n  <script src=\"script.js\"></script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/my.css",
    "content": "/* your styles */\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/source.view/script.js",
    "content": "let table = document.getElementById('bagua-table');\n\n/* your code */\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/4-edit-td-click/task.md",
    "content": "importance: 5\n\n---\n\n# Edit TD on click\n\nBuat sel tabel yang dapat diedit saat diklik.\n\n- Saat diklik -- sel akan menjadi \"editable\" (textarea muncul dalam), kita bisa mengubah HTML. Tidak boleh mengubah ukurannya, semua geometri harus tetap sama..\n- Tombol OK and CANCEL muncul di bawah sel untuk menyelesaikan atau membatalkan proses perubahan.\n- Hanya satu sel yang dapat diedit setiap saatya. Sementara `<td>` dalam \"edit mode\", klik pada sel lain akan diabaikan.\n- Tabel mungkin memilki banyak sel. Gunakan event delegation.\n\nDemo:\n\n[iframe src=\"solution\" height=400]\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.md",
    "content": "\nKita bisa gunakan `mouse.onclick` untuk menangani klik dan membuat mouse \"moveable/bergerak\" dengan `position:fixed`, kemudian `mouse.onkeydown` untuk yang menangani tombol panah.\n\nSatu-satunya jebakan ialah `keydown` hanya memicu pada elemen dengan fokus. Jadi kita perlu untuk menambahkan `tabindex` pada elemen. Karena kita dilarang mengubah HTML, kita bisa gunakan  properti `mouse.tabIndex` untuk itu.\n\nP.S. Kita juga bisa ganti `mouse.onclick` dengan `mouse.onfocus`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #mouse {\n      display: inline-block;\n      cursor: pointer;\n      margin: 0;\n    }\n\n    #mouse:focus {\n      outline: 1px dashed black;\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>Click on the mouse and move it with arrow keys.</p>\n\n  <pre id=\"mouse\">\n _   _\n(q\\_/p)\n /. .\\\n=\\_t_/=   __\n /   \\   (\n((   ))   )\n/\\) (/\\  /\n\\  Y  /-'\n nn^nn\n</pre>\n\n\n  <script>\n    mouse.tabIndex = 0;\n    \n    mouse.onclick = function() {\n      this.style.left = this.getBoundingClientRect().left + 'px';\n      this.style.top = this.getBoundingClientRect().top + 'px';\n\n      this.style.position = 'fixed';\n    };\n\n\n    mouse.onkeydown = function(e) {\n      switch (e.key) {\n        case 'ArrowLeft':\n          this.style.left = parseInt(this.style.left) - this.offsetWidth + 'px';\n          return false;\n        case 'ArrowUp':\n          this.style.top = parseInt(this.style.top) - this.offsetHeight + 'px';\n          return false;\n        case 'ArrowRight':\n          this.style.left = parseInt(this.style.left) + this.offsetWidth + 'px';\n          return false;\n        case 'ArrowDown':\n          this.style.top = parseInt(this.style.top) + this.offsetHeight + 'px';\n          return false;\n      }\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    #mouse {\n      display: inline-block;\n      cursor: pointer;\n      margin: 0;\n    }\n\n    #mouse:focus {\n      outline: 1px dashed black;\n    }\n  </style>\n</head>\n\n<body>\n\n  <p>Click on the mouse and move it with arrow keys.</p>\n\n  <pre id=\"mouse\">\n _   _\n(q\\_/p)\n /. .\\\n=\\_t_/=   __\n /   \\   (\n((   ))   )\n/\\) (/\\  /\n\\  Y  /-'\n nn^nn\n</pre>\n\n\n  <script>\n    // ...your code...\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/5-keyboard-mouse/task.md",
    "content": "importance: 4\n\n---\n\n# Keyboard-driven mouse\n\nFokus pada mouse. Lalu gunakan tombol arah untuk menggerakkannya:\n\n[demo src=\"solution\"]\n\nP.S. Jangan letakkan event handler dimanapun kecuali pada elemen `#mouse`.\nP.P.S. Jangan modifikasi HTML/CSS, pendekatannya harus umum dan bekerja dengan elemen manapun.\n"
  },
  {
    "path": "2-ui/4-forms-controls/2-focus-blur/article.md",
    "content": "# Focusing: focus/blur\n\nSebuah elemen menjadi fokus ketika user mengkliknya atau menggunakan tombol `key:Tab` pada keyboard. Ada juga atribut HTML `autofocus` yang fokus pada sebuah element secara default ketika memuat sebuah halaman dan sebagainya untuk mendapatkan fokus.\n\nFokus pada sebuah elemen pada umumnya diartikan: \"bersiap untuk terima datanya disini\", jadi itu adalah momen ketika kita bisa menjalankan kode untuk di inisiasi kebutuhan fungsionalitas.\n\nMomen ketika hilang fokus (\"blur\") bahkan bisa lebih penting. Itu adalah dimana user klik disuatu tempat or menekan `key:Tab` untuk pindah ke kotak selanjutnya, atau dengan cara yang lain.\n\nKehilangan fokus pada umumnya diartikan: \"data telah di isi\", jadi kita bisa menjalankan kode untuk mengeceknya atau bahkan menyimpannya ke server dan sebagainya.\n\nAda beberapa keanehan saat bekerja dengan event fokus. Kami akan melakukan yang terbaik untuk membahasnya lebih lanjut.\n\n## Events focus/blur\n\nEvent `focus` terpanggil/trigger saat sedang fokus, dan event `blur` -- ketika elemen hilang fokus.\n\nMari gunakan mereka sebagai validasi sebuah kotak input.\n\nDalam contoh dibawah:\n\n- `blur` cek jika kotak email telah terisi , dan jika tidak -- tampilkan error.\n- `focus` menyembunyikan pesan error (pada `blur` itu akan di cek kembali):\n\n```html run autorun height=60\n<style>\n  .invalid {\n    border-color: red;\n  }\n  #error {\n    color: red;\n  }\n</style>\n\nYour email please: <input type=\"email\" id=\"input\" />\n\n<div id=\"error\"></div>\n\n<script>\n  *!*input.onblur*/!* = function() {\n    if (!input.value.includes('@')) { // not email\n      input.classList.add('invalid');\n      error.innerHTML = 'Please enter a correct email.'\n    }\n  };\n\n  *!*input.onfocus*/!* = function() {\n    if (this.classList.contains('invalid')) {\n      // remove the \"error\" indication, because the user wants to re-enter something\n      this.classList.remove('invalid');\n      error.innerHTML = \"\";\n    }\n  };\n</script>\n```\n\nDengan HTML modern kita bisa melakukan beberapa validasi menggunakan atribut input: `required`, `pattern` dan lainnya. Dan terkadang hanya mereka yang kita butuhkan. Kita bisa menggunakan Javascricpt jika ingin fleksibelitas lebih. Juga kita bisa secara otomatis mengirim nilai yang diubah ke server jika nilainya benar.\n\n## Metode focus/blur\n\nMetode `elem.focus()` dan `elem.blur()` set/unset fokus pada elemen.\n\nMisalnya, mari buat pengunjung tidak bisa keluar dari input jika nilainya tidak valid:\n\n```html run autorun height=80\n<style>\n  .error {\n    background: red;\n  }\n</style>\n\nYour email please: <input type=\"email\" id=\"input\" />\n<input\n  type=\"text\"\n  style=\"width:220px\"\n  placeholder=\"make email invalid and try to focus here\"\n/>\n\n<script>\n    input.onblur = function() {\n      if (!this.value.includes('@')) { // not email\n        // show the error\n        this.classList.add(\"error\");\n  *!*\n        // ...and put the focus back\n        input.focus();\n  */!*\n      } else {\n        this.classList.remove(\"error\");\n      }\n    };\n</script>\n```\n\nItu bekerja pada semua browser kecuali Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=53579)).\n\nJika kita sedang mengetik/memasukkan sesuatu ke input dan coba menggunakan`key:Tab` atau klik diluar elemen `<input>`, maka `onblur` membuat fokus kembali ke input.\n\nPerlu diingat bahwa kita tidak bisa \"mencegah hilangnya fokus\" dengan memanggil `event.preventDefault()` pada `onblur`, karena `onblur` bekerja saat element hilang fokus.\n\n```warn header=\"JavaScript-initiated focus loss\"\nFocus loss bisa terjadi untuk alasan tertentu.\n\nSalah satu diantaranya adalah ketika pengunjung klik di tempat lain. Tetapi mungkin Javascript sendiri yang menyebabkannya, Misalya:\n\n- Sebuah `alert` memindahkan fokus untuknya, jadi itu menyebabkan focus loss pada elemen (`blur` event), dan ketika `alert` sudah tidak ada, fokus ada kembali (`focus` event).\n- Jika sebuah elemen dihapus dari DOM, itu juga menyebabkan focus loss. Jika di isi lagi nanti, maka fokus tidak akan kembali.\n\nBeberapa fitur ini membuat `focus/blur` handler menjadi misbehave -- trigger disaat mereka tidak diperlukan.\n\nResep yang baik adalah berhati-hati mengunakan event ini. Jika kita ingin melacak focus-loss yang dimulai oleh user, maka kita harus menghidari yang dapat menyebabkan pada kita sendiri.\n```\n\n## Memungkinkan fokus pada elemen apapun: tabindex\n\nSecara default banyak elemen yang tidak support focusing.\n\nDaftarnya sedikit bervariasi dibeda browser, etapi satu hal yang pasti benar: `focus/blur` dukungannya terjamin untuk elemen-elemen yang pengunjung bisa berinteraksi dengan: `<button>`, `<input>`, `<select>`, `<a>` dan lainnya.\n\nDi lain sisi, elemen-elemen yang ada hanya untuk meformat sesuatu seperti `<div>`, `<span>`, `<table>` -- adalah _unfocusable_ secara default. Metode `elem.focus()` tidak bekerja pada mereka, dan `focus/blur` event tidak akan pernah ke trigger.\n\nIni bisa diubah dengan menggunakan HTML-attribute `tabindex`.\n\nElemen apapun menjadi _focusable_ jika ia memilki`tabindex`. Nilai atributnya adalah dari urutan nomor elemen ketika `key:Tab` digunakan untuk berpindah diantara mereka.\n\nItu adalah: jika kita memilki 2 elemen, yang pertama memilki `tabindex=\"1\"`, dan yang kedua memilki `tabindex=\"2\"`, lalu menekan `key:Tab` pada saat masih di elemen pertama -- fokus berpindah ke elemen kedua.\n\nUrutuan pindahnya ialah: elemen dengan `tabindex` dari `1` dan diatasnya menjadi yang pertama (pada urutan`tabindex`), dan baru kemudian elemen tanpa `tabindex` (seperti `<input>` input biasa).\n\nElement dengan `tabindex` yang sesuai berpindah pada urutan sumber dokumen (urutan default).\n\nDisana ada dua nilai khusus:\n\n- `tabindex=\"0\"` menempatkan sebuah elemen diantara mereka tanpa `tabindex`. Itu ialah, ketika kita pindah elemen, elemen dengan `tabindex=0` berpindah setelah elemen dengan `tabindex ≥ 1`.\n\n  Biasanya itu digunakan agar sebuah elemen menjadi _focusable_, tapi tetap memerhatikan urutan perpindahan default. Untuk membuat sebuah elemen menjadi bagian dari form yang setara`<input>`.\n\n- `tabindex=\"-1\"` hanya membolehkan _programmatic focusing_ pada sebuah elemen. Kunci `key:Tab` mengabaikan elemen seperti itu, akan tetapi metode `elem.focus()` dapat berfungsi.\n\nMisalnya, ada list elemen. Klik item pertama dan tekan `key:Tab`:\n\n```html autorun no-beautify\nKlik pada elemen pertama dan tekan tab. Perhatikan pada urutan. Harap perhatikan\nbahwa banyak Tab berikutnya yang dapat memindahkan fokus dari iframe dalam\ncontoh.\n<ul>\n  <li tabindex=\"1\">One</li>\n  <li tabindex=\"0\">Zero</li>\n  <li tabindex=\"2\">Two</li>\n  <li tabindex=\"-1\">Minus one</li>\n</ul>\n\n<style>\n  li {\n    cursor: pointer;\n  }\n  :focus {\n    outline: 1px dashed green;\n  }\n</style>\n```\n\nUrutannya sepeti ini: `1 - 2 - 0`. Normalnya, `<li>` tidak support _focusing_, tetapi dengan `tabindex`membuatnya _focusable_, berserta dengan eventnya dan styling `:focus`.\n\n```smart header=\"The property `elem.tabIndex`juga dapat bekerja\" Kita bisa menambahkan`tabindex`dari JavaScript dengan menggunakan properti`elem.tabIndex`. Itu menghasilkan efek yang sama.\n\n````\n\n## Delegation: focusin/focusout\n\nEvents `focus` and `blur` tidak mengelembung(bubble)./\nMisalnya, kita tidak bisa letak `onfocus` pada `<form>` untuk menghighlight-nya, seperti ini:\n\n```html autorun height=80\n<!-- on focusing in the form -- add the class -->\n<form *!*onfocus=\"this.className='focused'\"*/!*>\n  <input type=\"text\" name=\"name\" value=\"Name\">\n  <input type=\"text\" name=\"surname\" value=\"Surname\">\n</form>\n\n<style> .focused { outline: 1px solid red; } </style>\n````\n\nContoh diatas tidak akan bekerja, karena ketika sedang fokus pada sebuah `<input>`, event `focus` akan trigger hanya pada input tersebut. Ia tidak mengelembung ke atas(bubble up). Jadi `form.onfocus` tidak akan pernah trigger.\n\nHanya ada dua solusi.\n\nPertama, ada satu sejarah lucu dengan fitur: `focus/blur` yang tidak mengelembung ke atas(bubble up), tetapi merambat ke bawah saat _capturing phase_.\n\nIni akan bekerja:\n\n```html autorun height=80\n<form id=\"form\">\n  <input type=\"text\" name=\"name\" value=\"Name\" />\n  <input type=\"text\" name=\"surname\" value=\"Surname\" />\n</form>\n\n<style>\n  .focused {\n    outline: 1px solid red;\n  }\n</style>\n\n<script>\n  *!*\n    // meletakkan handler pada capturing phase (argumenterakhir set menjadit true)\n    form.addEventListener(\"focus\", () => form.classList.add('focused'), true);\n    form.addEventListener(\"blur\", () => form.classList.remove('focused'), true);\n  */!*\n</script>\n```\n\nKedua, ada event `focusin` dan `focusout` -- persis sama dengan`focus/blur`, tetapi mereka mengelembung(bubble).\nIngat bahwa mereka perlu di definisi menggunakan `elem.addEventListener`, bukan `on<event>`.\n\nJadi ini adalah cara lain yang dapat bekerja:\n\n```html autorun height=80\n<form id=\"form\">\n  <input type=\"text\" name=\"name\" value=\"Name\" />\n  <input type=\"text\" name=\"surname\" value=\"Surname\" />\n</form>\n\n<style>\n  .focused {\n    outline: 1px solid red;\n  }\n</style>\n\n<script>\n  *!*\n    form.addEventListener(\"focusin\", () => form.classList.add('focused'));\n    form.addEventListener(\"focusout\", () => form.classList.remove('focused'));\n  */!*\n</script>\n```\n\n## Kesimpulan\n\nEvent `focus` dan `blur` trigger pada saat sebuah elemen fokus dan hilang fokus.\n\nKeistimewaan mereka adalah:\n\n- Mereka tidak mengelembung(bubble). Gantinya bisa menggunakan _capturing state_ atau `focusin/focusout`.\n- Kebanyakan elemen tidak mendukung fokus secara default. Gunakan `tabindex` untuk membuat elemen manapapun menjadi _focusable_.\n\nElemen fokus saat ini tersedia sebagai `document.activeElement`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.md",
    "content": ""
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    td select,\n    td input {\n      width: 150px;\n    }\n\n    #diagram td {\n      vertical-align: bottom;\n      text-align: center;\n      padding: 10px;\n    }\n\n    #diagram div {\n      margin: auto;\n    }\n  </style>\n</head>\n\n<body>\n\n  Deposit calculator.\n\n  <form name=\"calculator\">\n    <table>\n      <tr>\n        <td>Initial deposit</td>\n        <td>\n          <input name=\"money\" type=\"number\" value=\"10000\" required>\n        </td>\n      </tr>\n      <tr>\n        <td>How many months?</td>\n        <td>\n          <select name=\"months\">\n            <option value=\"3\">3 (minimum)</option>\n            <option value=\"6\">6 (half-year)</option>\n            <option value=\"12\" selected>12 (one year)</option>\n            <option value=\"18\">18 (1.5 years)</option>\n            <option value=\"24\">24 (2 years)</option>\n            <option value=\"30\">30 (2.5 years)</option>\n            <option value=\"36\">36 (3 years)</option>\n            <option value=\"60\">60 (5   years)</option>\n          </select>\n        </td>\n      </tr>\n      <tr>\n        <td>Interest per year?</td>\n        <td>\n          <input name=\"interest\" type=\"number\" value=\"5\" required>\n        </td>\n      </tr>\n    </table>\n\n\n  </form>\n\n\n  <table id=\"diagram\">\n    <tr>\n      <th>Was:</th>\n      <th>Becomes:</th>\n    </tr>\n    <tr>\n      <th id=\"money-before\"></th>\n      <th id=\"money-after\"></th>\n    </tr>\n    <td>\n      <div style=\"background: red;width:40px;height:100px\"></div>\n    </td>\n    <td>\n      <div style=\"background: green;width:40px;height:0\" id=\"height-after\"></div>\n    </td>\n  </table>\n\n  <script>\n\n    let form = document.forms.calculator;\n\n    form.money.oninput = calculate;\n    form.months.onchange = calculate;\n    form.interest.oninput = calculate;\n\n    function calculate() {\n      let initial = +form.money.value;\n      if (!initial) return;\n\n      let interest = form.interest.value * 0.01;\n\n      if (!interest) return;\n\n      let years = form.months.value / 12;\n      if (!years) return;\n\n      let result = Math.round(initial * (1 + interest * years));\n\n      let height = result / form.money.value * 100 + 'px';\n      document.getElementById('height-after').style.height = height;\n      document.getElementById('money-before').innerHTML = form.money.value;\n      document.getElementById('money-after').innerHTML = result;\n    }\n\n    calculate();\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    td select,\n    td input {\n      width: 150px;\n    }\n\n    #diagram td {\n      vertical-align: bottom;\n      text-align: center;\n      padding: 10px;\n    }\n\n    #diagram div {\n      margin: auto;\n    }\n  </style>\n</head>\n\n<body>\n\n  Deposit calculator.\n\n  <form name=\"calculator\">\n    <table>\n      <tr>\n        <td>Initial deposit</td>\n        <td>\n          <input name=\"money\" type=\"number\" value=\"10000\" required>\n        </td>\n      </tr>\n      <tr>\n        <td>How many months?</td>\n        <td>\n          <select name=\"months\">\n            <option value=\"3\">3 (minimum)</option>\n            <option value=\"6\">6 (half-year)</option>\n            <option value=\"12\" selected>12 (one year)</option>\n            <option value=\"18\">18 (1.5 years)</option>\n            <option value=\"24\">24 (2 years)</option>\n            <option value=\"30\">30 (2.5 years)</option>\n            <option value=\"36\">36 (3 years)</option>\n            <option value=\"60\">60 (5   years)</option>\n          </select>\n        </td>\n      </tr>\n      <tr>\n        <td>Interest per year?</td>\n        <td>\n          <input name=\"interest\" type=\"number\" value=\"5\" required>\n        </td>\n      </tr>\n    </table>\n\n\n  </form>\n\n\n  <table id=\"diagram\">\n    <tr>\n      <th>Was:</th>\n      <th>Becomes:</th>\n    </tr>\n    <tr>\n      <th id=\"money-before\"></th>\n      <th id=\"money-after\"></th>\n    </tr>\n    <td>\n      <div style=\"background: red;width:40px;height:100px\"></div>\n    </td>\n    <td>\n      <div style=\"background: green;width:40px;height:0\" id=\"height-after\"></div>\n    </td>\n  </table>\n\n  <script>\n\n    let form = document.forms.calculator;\n\n    // your code\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/1-deposit-calculator/task.md",
    "content": "importance: 5\n\n---\n\n# Deposit calculator\n\nCreate an interface that allows to enter a sum of bank deposit and percentage, then calculates how much it will be after given periods of time.\n\nHere's the demo:\n\n[iframe src=\"solution\" height=\"350\" border=\"1\"]\n\nAny input change should be processed immediately.\n\nThe formula is:\n```js\n// initial: the initial money sum\n// interest: e.g. 0.05 means 5% per year\n// years: how many years to wait\nlet result = Math.round(initial * (1 + interest * years));\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/3-events-change-input/article.md",
    "content": "# Events: change, input, cut, copy, paste\n\nMari kita bahas berbagai *event* yang menyertai pembaruan data.\n\n## Event: change\n\n*Event* `change` terpicu saat elemen selesai diubah.\n\nUntuk input teks, itu berarti bahwa *event* terjadi ketika kehilangan fokus.\n\nMisalnya, saat kita mengetik di *field* teks di bawah -- tidak ada *event*. Tetapi ketika kita memindahkan fokus ke tempat lain, misalnya, klik tombol -- akan ada *event* `change`:\n\n```html autorun height=40 run\n<input type=\"text\" onchange=\"alert(this.value)\">\n<input type=\"button\" value=\"Button\">\n```\n\nUntuk elemen lain: `select`, `input type=checkbox/radio` yang dipicu tepat setelah pilihan berubah:\n\n```html autorun height=40 run\n<select onchange=\"alert(this.value)\">\n  <option value=\"\">Select something</option>\n  <option value=\"1\">Option 1</option>\n  <option value=\"2\">Option 2</option>\n  <option value=\"3\">Option 3</option>\n</select>\n```\n\n\n## Event: input\n\n*Event* `input` terpicu setiap kali nilai telah diubah oleh pengguna.\n\nTidak seperti *event* keyboard, *event* `input` memicu perubahan *value* apa pun, bahkan yang tidak melibatkan tindakan keyboard: menempelkan dengan mouse atau menggunakan pengenalan suara untuk mendikte teks.\n\nContohnya:\n\n```html autorun height=40 run\n<input type=\"text\" id=\"input\"> oninput: <span id=\"result\"></span>\n<script>\n  input.oninput = function() {\n    result.innerHTML = input.value;\n  };\n</script>\n```\n\nJika kita ingin menangani setiap modifikasi `<input>` maka *event* ini adalah pilihan terbaik.\n\nDi sisi lain, *event* `input` tidak dipicu pada input keyboard dan tindakan lain yang tidak melibatkan perubahan nilai, misalnya menekan tombol panah `key:⇦` `key:⇨` saat memasukkan.\n\n```smart header=\"Tidak dapat mencegah apa pun di `oninput`\"\n*Event* `input` terjadi setelah nilai diubah.\n\nJadi kita tidak bisa menggunakan `event.preventDefault()` disana -- terlalu terlambat, tidak akan ada efeknya.\n```\n\n## Events: cut, copy, paste\n\n*Event* ini terjadi saat memotong/menyalin/menempelkan nilai.\n\nMereka termasuk dalam kelas [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) dan menyediakan akses ke data yang disalin/ditempel.\n\nKita juga bisa menggunakan `event.preventDefault()` untuk membatalkan aksi, maka tidak akan ada yang disalin/ditempel.\n\nContohnya, kode di bawah ini mencegah semua *event* tersebut dan menunjukkan apa yang kita coba potong/salin/tempel:\n\n```html autorun height=40 run\n<input type=\"text\" id=\"input\">\n<script>\n  input.oncut = input.oncopy = input.onpaste = function(event) {\n    alert(event.type + ' - ' + event.clipboardData.getData('text/plain'));\n    return false;\n  };\n</script>\n```\n\nHarap dicatat, bahwa memungkinkan untuk salin/tempel tidak hanya teks, tetapi semuanya. Misalnya, kita dapat menyalin file di manajer file OS, dan menempelkannya.\n\nItu karena `clipboardData` mengimplementasikan *interface* `DataTransfer`, yang biasa digunakan untuk drag'n'drop dan salin/tempel. Ini sedikit di luar jangkauan kita sekarang, tetapi Anda dapat menemukan metodenya [dalam spesifikasi ini](https://html.spec.whatwg.org/multipage/dnd.html#the-datatransfer-interface).\n\n```warn header=\"ClipboardAPI: batasan keamanan pengguna\"\n*Clipboard* adalah hal \"global\" tingkat OS. Jadi sebagian besar peramban mengizinkan akses baca/tulis ke *clipboard* hanya dalam lingkup tindakan pengguna tertentu untuk keamanan, misalnya di *event handler* `onclick`.\n\nJuga dilarang untuk membuat *event* *clipboard* \"custom\" dengan `dispatchEvent` di semua peramban kecuali Firefox.\n```\n\n## Ringkasan\n\n*Event* perubahan data:\n\n| *Event* | Deskripsi | Specials |\n|---------|----------|-------------|\n| `change`| Sebuah *value* diubah. | Untuk pemicu input teks pada saat kehilangan fokus. |\n| `input` | Untuk input teks pada setiap perubahan. | Pemicu langsung tidak seperti `change`. |\n| `cut/copy/paste` | Tindakan potong/salin/tempel. | Tindakan tersebut dapat dicegah. Properti `event.clipboardData` memberikan akses baca/tulis ke *clipboard*. |\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.md",
    "content": "A modal window can be implemented using a half-transparent `<div id=\"cover-div\">` that covers the whole window, like this:\n\n```css\n#cover-div {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9000;\n  width: 100%;\n  height: 100%;\n  background-color: gray;\n  opacity: 0.3;\n}\n```\n\nBecause the `<div>` covers everything, it gets all clicks, not the page below it.\n\nAlso we can prevent page scroll by setting `body.style.overflowY='hidden'`.\n\nThe form should be not in the `<div>`, but next to it, because we don't want it to have `opacity`.\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body style=\"height:3000px\">\n\n  <h2>Click the button below</h2>\n\n  <input type=\"button\" value=\"Click to show the form\" id=\"show-button\">\n\n\n  <div id=\"prompt-form-container\">\n    <form id=\"prompt-form\">\n      <div id=\"prompt-message\"></div>\n      <input name=\"text\" type=\"text\">\n      <input type=\"submit\" value=\"Ok\">\n      <input type=\"button\" name=\"cancel\" value=\"Cancel\">\n    </form>\n  </div>\n\n  <script>\n    // Show a half-transparent DIV to \"shadow\" the page\n    // (the form is not inside, but near it, because it shouldn't be half-transparent)\n    function showCover() {\n      let coverDiv = document.createElement('div');\n      coverDiv.id = 'cover-div';\n\n      // make the page unscrollable while the modal form is open\n      document.body.style.overflowY = 'hidden';\n\n      document.body.append(coverDiv);\n    }\n\n    function hideCover() {\n      document.getElementById('cover-div').remove();\n      document.body.style.overflowY = '';\n    }\n\n    function showPrompt(text, callback) {\n      showCover();\n      let form = document.getElementById('prompt-form');\n      let container = document.getElementById('prompt-form-container');\n      document.getElementById('prompt-message').innerHTML = text;\n      form.text.value = '';\n\n      function complete(value) {\n        hideCover();\n        container.style.display = 'none';\n        document.onkeydown = null;\n        callback(value);\n      }\n\n      form.onsubmit = function() {\n        let value = form.text.value;\n        if (value == '') return false; // ignore empty submit\n\n        complete(value);\n        return false;\n      };\n\n      form.cancel.onclick = function() {\n        complete(null);\n      };\n\n      document.onkeydown = function(e) {\n        if (e.key == 'Escape') {\n          complete(null);\n        }\n      };\n\n      let lastElem = form.elements[form.elements.length - 1];\n      let firstElem = form.elements[0];\n\n      lastElem.onkeydown = function(e) {\n        if (e.key == 'Tab' && !e.shiftKey) {\n          firstElem.focus();\n          return false;\n        }\n      };\n\n      firstElem.onkeydown = function(e) {\n        if (e.key == 'Tab' && e.shiftKey) {\n          lastElem.focus();\n          return false;\n        }\n      };\n\n      container.style.display = 'block';\n      form.elements.text.focus();\n    }\n\n    document.getElementById('show-button').onclick = function() {\n      showPrompt(\"Enter something<br>...smart :)\", function(value) {\n        alert(\"You entered: \" + value);\n      });\n    };\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/solution.view/style.css",
    "content": "html,\nbody {\n  margin: 0;\n  padding: 0;\n  width: 100%;\n  height: 100%;\n}\n\n#prompt-form {\n  display: inline-block;\n  padding: 5px 5px 5px 70px;\n  width: 200px;\n  border: 1px solid black;\n  background: white url(https://en.js.cx/clipart/prompt.png) no-repeat left 5px;\n  vertical-align: middle;\n}\n\n#prompt-form-container {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9999;\n  display: none;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n\n#prompt-form-container:before {\n  display: inline-block;\n  height: 100%;\n  content: '';\n  vertical-align: middle;\n}\n\n#cover-div {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9000;\n  width: 100%;\n  height: 100%;\n  background-color: gray;\n  opacity: 0.3;\n}\n\n#prompt-form input[name=\"text\"] {\n  display: block;\n  margin: 5px;\n  width: 180px;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"prompt-form-container\">\n    <form id=\"prompt-form\">\n      <div id=\"prompt-message\">Enter something...\n        <br>Please..</div>\n      <input name=\"text\" type=\"text\">\n      <input type=\"submit\" value=\"Ok\">\n      <input type=\"button\" name=\"cancel\" value=\"Cancel\">\n    </form>\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/source.view/style.css",
    "content": "html,\nbody {\n  width: 100%;\n  height: 100%;\n  padding: 0;\n  margin: 0;\n}\n\n#prompt-form {\n  display: inline-block;\n  padding: 5px 5px 5px 70px;\n  width: 200px;\n  border: 1px solid black;\n  background: white url(https://en.js.cx/clipart/prompt.png) no-repeat left 5px;\n  vertical-align: middle;\n}\n\n#prompt-form-container {\n  position: fixed;\n  top: 0;\n  left: 0;\n  z-index: 9999;\n  width: 100%;\n  height: 100%;\n  text-align: center;\n}\n\n#prompt-form-container:before {\n  display: inline-block;\n  height: 100%;\n  content: '';\n  vertical-align: middle;\n}\n\n#prompt-form input[name=\"text\"] {\n  display: block;\n  margin: 5px;\n  width: 180px;\n}\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/1-modal-dialog/task.md",
    "content": "importance: 5\n\n---\n\n# Modal form\n\nCreate a function `showPrompt(html, callback)` that shows a form with the message `html`, an input field and buttons `OK/CANCEL`.\n\n- A user should type something into a text field and press `key:Enter` or the OK button, then `callback(value)` is called with the value they entered.\n- Otherwise if the user presses `key:Esc` or CANCEL, then `callback(null)` is called.\n\nIn both cases that ends the input process and removes the form.\n\nRequirements:\n\n- The form should be in the center of the window.\n- The form is *modal*. In other words, no interaction with the rest of the page is possible until the user closes it.\n- When the form is shown, the focus should be inside the `<input>` for the user.\n- Keys `key:Tab`/`key:Shift+Tab` should shift the focus between form fields, don't allow it to leave for other page elements.\n\nUsage example:\n\n```js\nshowPrompt(\"Enter something<br>...smart :)\", function(value) {\n  alert(value);\n});\n```\n\nA demo in the iframe:\n\n[iframe src=\"solution\" height=160 border=1]\n\nP.S. The source document has HTML/CSS for the form with fixed positioning, but it's up to you to make it modal.\n"
  },
  {
    "path": "2-ui/4-forms-controls/4-forms-submit/article.md",
    "content": "# Forms: *event* dan *method* submit\n\nEvent `submit` terpicu saat *form* dikirimkan, biasanya digunakan untuk memvalidasi *form* sebelum mengirimkannya ke *server* atau untuk membatalkan pengiriman dan memprosesnya dalam JavaScript.\n\n*Method* `form.submit()` memungkinkan untuk memulai pengiriman *form* dari JavaScript. Kita dapat menggunakannya untuk membuat dan mengirim *form* kita sendiri secara dinamis ke *server*.\n\nMari kita lihat lebih detail\n\n## Event: submit\n\nAda dua cara utama untuk mengirimkan *form*:\n\n1. Pertama -- untuk mengklik `<input type=\"submit\">` atau `<input type=\"image\">`.\n2. Kedua -- tekan `key:Enter` pada kolom input.\n\nKedua tindakan tersebut mengarah ke *event* `submit` pada *form*. *Handler* dapat memeriksa data, dan jika ada kesalahan, tunjukkan dan panggil `event.preventDefault()`, maka formulir tidak akan dikirim ke server.\n\nDalam *form* di bawah ini:\n1. Masuk ke *field* teks dan tekan `key: Enter`.\n2. Klik `<input type=\"submit\">`.\n\nKedua tindakan menunjukkan `alert` dan *form* tidak dikirim ke mana pun karena `return false`:\n\n```html autorun height=60 no-beautify\n<form onsubmit=\"alert('submit!');return false\">\n  First: Enter in the input field <input type=\"text\" value=\"text\"><br>\n  Second: Click \"submit\": <input type=\"submit\" value=\"Submit\">\n</form>\n```\n\n````smart header=\"Hubungan antara `submit` dan `click`\"\nSaat *form* dikirim menggunakan `key:Enter` pada *field* input, *event* `click` akan dipicu pada `<input type=\"submit\">`.\n\nItu agak lucu, karena tidak ada klik sama sekali.\n\nBerikut demonya:\n```html autorun height=60\n<form onsubmit=\"return false\">\n <input type=\"text\" size=\"30\" value=\"Focus here and press enter\">\n <input type=\"submit\" value=\"Submit\" *!*onclick=\"alert('click')\"*/!*>\n</form>\n```\n\n````\n\n## Method: submit\n\nUntuk mengirimkan *form* ke *server* secara manual, kita dapat memanggil `form.submit()`.\n\nMaka *event* `submit` tidak dibuat. Diasumsikan bahwa jika programmer memanggil `form.submit()`, maka skrip telah melakukan semua pemrosesan terkait.\n\nTerkadang itu digunakan untuk membuat dan mengirim formulir secara manual, seperti ini:\n\n```js run\nlet form = document.createElement('form');\nform.action = 'https://google.com/search';\nform.method = 'GET';\n\nform.innerHTML = '<input name=\"q\" value=\"test\">';\n\n// form harus berada di dalam dokumen untuk mengirimkannya.\ndocument.body.append(form);\n\nform.submit();\n```\n"
  },
  {
    "path": "2-ui/4-forms-controls/index.md",
    "content": "# Forms, controls\n\nSpecial properties and events for forms `<form>` and controls: `<input>`, `<select>` and other.\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/article.md",
    "content": "# Page: DOMContentLoaded, load, beforeunload, unload\n\nThe lifecycle of an HTML page has three important events:\n\n- `DOMContentLoaded` -- the browser fully loaded HTML, and the DOM tree is built, but external resources like pictures `<img>` and stylesheets may not yet have loaded.  \n- `load` -- not only HTML is loaded, but also all the external resources: images, styles etc.\n- `beforeunload/unload` -- the user is leaving the page.\n\nEach event may be useful:\n\n- `DOMContentLoaded` event -- DOM is ready, so the handler can lookup DOM nodes, initialize the interface.\n- `load` event -- external resources are loaded, so styles are applied, image sizes are known etc.\n- `beforeunload` event -- the user is leaving: we can check if the user saved the changes and ask them whether they really want to leave.\n- `unload` -- the user almost left, but we still can initiate some operations, such as sending out statistics.\n\nLet's explore the details of these events.\n\n## DOMContentLoaded\n\nThe `DOMContentLoaded` event happens on the `document` object.\n\nWe must use `addEventListener` to catch it:\n\n```js\ndocument.addEventListener(\"DOMContentLoaded\", ready);\n// not \"document.onDOMContentLoaded = ...\"\n```\n\nFor instance:\n\n```html run height=200 refresh\n<script>\n  function ready() {\n    alert('DOM is ready');\n\n    // image is not yet loaded (unless it was cached), so the size is 0x0\n    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);\n  }\n\n*!*\n  document.addEventListener(\"DOMContentLoaded\", ready);\n*/!*\n</script>\n\n<img id=\"img\" src=\"https://en.js.cx/clipart/train.gif?speed=1&cache=0\">\n```\n\nIn the example, the `DOMContentLoaded` handler runs when the document is loaded, so it can see all the elements, including `<img>` below.\n\nBut it doesn't wait for the image to load. So `alert` shows zero sizes.\n\nAt first sight, the `DOMContentLoaded` event is very simple. The DOM tree is ready -- here's the event. There are few peculiarities though.\n\n### DOMContentLoaded and scripts\n\nWhen the browser processes an HTML-document and comes across a `<script>` tag, it needs to execute before continuing building the DOM. That's a precaution, as scripts may want to modify DOM, and even `document.write` into it, so `DOMContentLoaded` has to wait.\n\nSo DOMContentLoaded definitely happens after such scripts:\n\n```html run\n<script>\n  document.addEventListener(\"DOMContentLoaded\", () => {\n    alert(\"DOM ready!\");\n  });\n</script>\n\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js\"></script>\n\n<script>\n  alert(\"Library loaded, inline script executed\");\n</script>\n```\n\nIn the example above, we first see \"Library loaded...\", and then \"DOM ready!\" (all scripts are executed).\n\n```warn header=\"Scripts that don't block DOMContentLoaded\"\nThere are two exceptions from this rule:\n1. Scripts with the `async` attribute, that we'll cover [a bit later](info:script-async-defer), don't block `DOMContentLoaded`.\n2. Scripts that are generated dynamically with `document.createElement('script')` and then added to the webpage also don't block this event.\n```\n\n### DOMContentLoaded and styles\n\nExternal style sheets don't affect DOM, so `DOMContentLoaded` does not wait for them.\n\nBut there's a pitfall. If we have a script after the style, then that script must wait until the stylesheet loads:\n\n```html run\n<link type=\"text/css\" rel=\"stylesheet\" href=\"style.css\">\n<script>\n  // the script doesn't not execute until the stylesheet is loaded\n  alert(getComputedStyle(document.body).marginTop);\n</script>\n```\n\nThe reason for this is that the script may want to get coordinates and other style-dependent properties of elements, like in the example above. Naturally, it has to wait for styles to load.\n\nAs `DOMContentLoaded` waits for scripts, it now waits for styles before them as well.\n\n### Built-in browser autofill\n\nFirefox, Chrome and Opera autofill forms on `DOMContentLoaded`.\n\nFor instance, if the page has a form with login and password, and the browser remembered the values, then on `DOMContentLoaded` it may try to autofill them (if approved by the user).\n\nSo if `DOMContentLoaded` is postponed by long-loading scripts, then autofill also awaits. You probably saw that on some sites (if you use browser autofill) -- the login/password fields don't get autofilled immediately, but there's a delay till the page fully loads. That's actually the delay until the `DOMContentLoaded` event.\n\n\n## window.onload [#window-onload]\n\nThe `load` event on the `window` object triggers when the whole page is loaded including styles, images and other resources. This event is available via the `onload` property.\n\nThe example below correctly shows image sizes, because `window.onload` waits for all images:\n\n```html run height=200 refresh\n<script>\n  window.onload = function() { // same as window.addEventListener('load', (event) => {\n    alert('Page loaded');\n\n    // image is loaded at this time\n    alert(`Image size: ${img.offsetWidth}x${img.offsetHeight}`);\n  };\n</script>\n\n<img id=\"img\" src=\"https://en.js.cx/clipart/train.gif?speed=1&cache=0\">\n```\n\n## window.onunload\n\nWhen a visitor leaves the page, the `unload` event triggers on `window`. We can do something there that doesn't involve a delay, like closing related popup windows.\n\nThe notable exception is sending analytics.\n\nLet's say we gather data about how the page is used: mouse clicks, scrolls, viewed page areas, and so on.\n\nNaturally, `unload` event is when the user leaves us, and we'd like to save the data on our server.\n\nThere exists a special `navigator.sendBeacon(url, data)` method for such needs, described in the specification <https://w3c.github.io/beacon/>.\n\nIt sends the data in background. The transition to another page is not delayed: the browser leaves the page, but still performs `sendBeacon`.\n\nHere's how to use it:\n```js\nlet analyticsData = { /* object with gathered data */ };\n\nwindow.addEventListener(\"unload\", function() {\n  navigator.sendBeacon(\"/analytics\", JSON.stringify(analyticsData));\n});\n```\n\n- The request is sent as POST.\n- We can send not only a string, but also forms and other formats, as described in the chapter <info:fetch>, but usually it's a stringified object.\n- The data is limited by 64kb.\n\nWhen the `sendBeacon` request is finished, the browser probably has already left the document, so there's no way to get server response (which is usually empty for analytics).\n\nThere's also a `keepalive` flag for doing such \"after-page-left\" requests in  [fetch](info:fetch) method for generic network requests. You can find more information in the chapter <info:fetch-api>.\n\n\nIf we want to cancel the transition to another page, we can't do it here. But we can use another event -- `onbeforeunload`.\n\n## window.onbeforeunload [#window.onbeforeunload]\n\nIf a visitor initiated navigation away from the page or tries to close the window, the `beforeunload` handler asks for additional confirmation.\n\nIf we cancel the event, the browser may ask the visitor if they are sure.\n\nYou can try it by running this code and then reloading the page:\n\n```js run\nwindow.onbeforeunload = function() {\n  return false;\n};\n```\n\nFor historical reasons, returning a non-empty string also counts as canceling the event. Some time ago browsers used to show it as a message, but as the [modern specification](https://html.spec.whatwg.org/#unloading-documents) says, they shouldn't.\n\nHere's an example:\n\n```js run\nwindow.onbeforeunload = function() {\n  return \"There are unsaved changes. Leave now?\";\n};\n```\n\nThe behavior was changed, because some webmasters abused this event handler by showing misleading and annoying messages. So right now old browsers still may show it as a message, but aside of that -- there's no way to customize the message shown to the user.\n\n## readyState\n\nWhat happens if we set the `DOMContentLoaded` handler after the document is loaded?\n\nNaturally, it never runs.\n\nThere are cases when we are not sure whether the document is ready or not. We'd like our function to execute when the DOM is loaded, be it now or later.\n\nThe `document.readyState` property tells us about the current loading state.\n\nThere are 3 possible values:\n\n- `\"loading\"` -- the document is loading.\n- `\"interactive\"` -- the document was fully read.\n- `\"complete\"` -- the document was fully read and all resources (like images) are loaded too.\n\nSo we can check `document.readyState` and setup a handler or execute the code immediately if it's ready.\n\nLike this:\n\n```js\nfunction work() { /*...*/ }\n\nif (document.readyState == 'loading') {\n  // still loading, wait for the event\n  document.addEventListener('DOMContentLoaded', work);\n} else {\n  // DOM is ready!\n  work();\n}\n```\n\nThere's also the `readystatechange` event that triggers when the state changes, so we can print all these states like this:\n\n```js run\n// current state\nconsole.log(document.readyState);\n\n// print state changes\ndocument.addEventListener('readystatechange', () => console.log(document.readyState));\n```\n\nThe `readystatechange` event is an alternative mechanics of tracking the document loading state, it appeared long ago. Nowadays, it is rarely used.\n\nLet's see the full events flow for the completeness.\n\nHere's a document with `<iframe>`, `<img>` and handlers that log events:\n\n```html\n<script>\n  log('initial readyState:' + document.readyState);\n\n  document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));\n  document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));\n\n  window.onload = () => log('window onload');\n</script>\n\n<iframe src=\"iframe.html\" onload=\"log('iframe onload')\"></iframe>\n\n<img src=\"http://en.js.cx/clipart/train.gif\" id=\"img\">\n<script>\n  img.onload = () => log('img onload');\n</script>\n```\n\nThe working example is [in the sandbox](sandbox:readystate).\n\nThe typical output:\n1. [1] initial readyState:loading\n2. [2] readyState:interactive\n3. [2] DOMContentLoaded\n4. [3] iframe onload\n5. [4] img onload\n6. [4] readyState:complete\n7. [4] window onload\n\nThe numbers in square brackets denote the approximate time of when it happens. Events labeled with the same digit happen approximately at the same time (+- a few ms).\n\n- `document.readyState` becomes `interactive` right before `DOMContentLoaded`. These two things actually mean the same.\n- `document.readyState` becomes `complete` when all resources (`iframe` and `img`) are loaded. Here we can see that it happens in about the same time as `img.onload` (`img` is the last resource) and `window.onload`. Switching to `complete` state means the same as `window.onload`. The difference is that `window.onload` always works after all other `load` handlers.\n\n\n## Summary\n\nPage load events:\n\n- The `DOMContentLoaded` event triggers on `document` when the DOM is ready. We can apply JavaScript to elements at this stage.\n  - Script such as `<script>...</script>` or `<script src=\"...\"></script>` block DOMContentLoaded, the browser waits for them to execute.\n  - Images and other resources may also still continue loading.\n- The `load` event on `window` triggers when the page and all resources are loaded. We rarely use it, because there's usually no need to wait for so long.\n- The `beforeunload` event on `window` triggers when the user wants to leave the page. If we cancel the event, browser asks whether the user really wants to leave (e.g we have unsaved changes).\n- The `unload` event on `window` triggers when the user is finally leaving, in the handler we can only do simple things that do not involve delays or asking a user. Because of that limitation, it's rarely used. We can send out a network request with `navigator.sendBeacon`.\n- `document.readyState` is the current state of the document, changes can be tracked in the `readystatechange` event:\n  - `loading` -- the document is loading.\n  - `interactive` -- the document is parsed, happens at about the same time as `DOMContentLoaded`, but before it.\n  - `complete` -- the document and resources are loaded, happens at about the same time as `window.onload`, but before it.\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/iframe.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  test iframe\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/readystate.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <!-- the sequence should be like this:\n  [10] initial readyState:loading\n  [20] readyState:interactive\n  [21] DOMContentLoaded\n  [30] iframe onload\n  [40] readyState:complete\n  [40] img onload\n  [40] window onload\n  -->\n\n  <div id=\"events\"></div>\n\n  <script>\n    function log(txt) {\n      events.insertAdjacentHTML('beforeend', `<div>[${Math.floor(performance.now())}] ${txt}</div>`);\n    }\n\n    log('initial readyState:' + document.readyState);\n\n    document.addEventListener('readystatechange', () => log('readyState:' + document.readyState));\n    document.addEventListener('DOMContentLoaded', () => log('DOMContentLoaded'));\n\n    window.onload = () => log('window onload');\n  </script>\n\n  <iframe src=\"iframe.html?speed=1&cache=0\" style=\"visibility:hidden\" onload=\"log('iframe onload')\"></iframe>\n\n  <img src=\"http://en.js.cx/clipart/train.gif?speed=1&cache=0\" id=\"img\" style=\"position:absolute;right:0;top:0\">\n  <script>\n    img.onload = () => log('img onload');\n  </script>\n\n  <div style=\"visibility:hidden\">\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n    text to make the document load longer text to make the document load longer\n  </div>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/01-onload-ondomcontentloaded/window-onbeforeunload.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n  <script>\n    function setHandler() {\n      window.onbeforeunload = function() {\n        return \"There are unsaved changes. Leave now?\";\n      };\n    }\n  </script>\n\n  <button onclick=\"setHandler()\">Set window.onbeforeunload</button>\n\n  <a href=\"http://example.com\">Leave for EXAMPLE.COM</a>\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/article.md",
    "content": "\n# Scripts: async, defer\n\nIn modern websites, scripts are often \"heavier\" than HTML: their download size is larger, and processing time is also longer.\n\nWhen the browser loads HTML and comes across a `<script>...</script>` tag, it can't continue building the DOM. It must execute the script right now. The same happens for external scripts `<script src=\"...\"></script>`: the browser must wait for the script to download, execute the downloaded script, and only then can it process the rest of the page.\n\nThat leads to two important issues:\n\n1. Scripts can't see DOM elements below them, so they can't add handlers etc.\n2. If there's a bulky script at the top of the page, it \"blocks the page\". Users can't see the page content till it downloads and runs:\n\n```html run height=100\n<p>...content before script...</p>\n\n<script src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<!-- This isn't visible until the script loads -->\n<p>...content after script...</p>\n```\n\nThere are some workarounds to that. For instance, we can put a script at the bottom of the page. Then it can see elements above it, and it doesn't block the page content from showing:\n\n```html\n<body>\n  ...all content is above the script...\n\n  <script src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n</body>\n```\n\nBut this solution is far from perfect. For example, the browser notices the script (and can start downloading it) only after it downloaded the full HTML document. For long HTML documents, that may be a noticeable delay.\n\nSuch things are invisible for people using very fast connections, but many people in the world still have slow internet speeds and use a far-from-perfect mobile internet connection.\n\nLuckily, there are two `<script>` attributes that solve the problem for us: `defer` and `async`.\n\n## defer\n\nThe `defer` attribute tells the browser not to wait for the script. Instead, the browser will continue to process the HTML, build DOM. The script loads \"in the background\", and then runs when the DOM is fully built.\n\nHere's the same example as above, but with `defer`:\n\n```html run height=100\n<p>...content before script...</p>\n\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<!-- visible immediately -->\n<p>...content after script...</p>\n```\n\nIn other words:\n\n- Scripts with `defer` never block the page.\n- Scripts with `defer` always execute when the DOM is ready (but before `DOMContentLoaded` event).\n\nThe following example demonstrates the second part:\n\n```html run height=100\n<p>...content before scripts...</p>\n\n<script>\n  document.addEventListener('DOMContentLoaded', () => alert(\"DOM ready after defer!\"));\n</script>\n\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js?speed=1\"></script>\n\n<p>...content after scripts...</p>\n```\n\n1. The page content shows up immediately.\n2. `DOMContentLoaded` event handler waits for the deferred script. It only triggers when the script is downloaded and executed.\n\n**Deferred scripts keep their relative order, just like regular scripts.**\n\nLet's say, we have two deferred scripts: the `long.js` and then `small.js`:\n\n```html\n<script defer src=\"https://javascript.info/article/script-async-defer/long.js\"></script>\n<script defer src=\"https://javascript.info/article/script-async-defer/small.js\"></script>\n```\n\nBrowsers scan the page for scripts and download them in parallel, to improve performance. So in the example above both scripts download in parallel. The `small.js` probably finishes first.\n\n...But the `defer` attribute, besides telling the browser \"not to block\", ensures that the relative order is kept. So even though `small.js` loads first, it still waits and runs after `long.js` executes.\n\nThat may be important for cases when we need to load a JavaScript library and then a script that depends on it.\n\n```smart header=\"The `defer` attribute is only for external scripts\"\nThe `defer` attribute is ignored if the `<script>` tag has no `src`.\n```\n\n## async\n\nThe `async` attribute is somewhat like `defer`. It also makes the script non-blocking. But it has important differences in the behavior.\n\nThe `async` attribute means that a script is completely independent:\n\n- The browser doesn't block on `async` scripts (like `defer`).\n- Other scripts don't wait for `async` scripts, and `async` scripts don't wait for them.\n- `DOMContentLoaded` and async scripts don't wait for each other:\n    - `DOMContentLoaded` may happen both before an async script (if an async script finishes loading after the page is complete)\n    - ...or after an async script (if an async script is short or was in HTTP-cache)\n\nIn other words, `async` scripts load in the background and run when ready. The DOM and other scripts don't wait for them, and they don't wait for anything. A fully independent script that runs when loaded. As simple, as it can get, right?\n\nHere's an example similar to what we've seen with `defer`: two scripts `long.js` and `small.js`, but now with `async` instead of `defer`.\n\nThey don't wait for each other. Whatever loads first (probably `small.js`) -- runs first:\n\n```html run height=100\n<p>...content before scripts...</p>\n\n<script>\n  document.addEventListener('DOMContentLoaded', () => alert(\"DOM ready!\"));\n</script>\n\n<script async src=\"https://javascript.info/article/script-async-defer/long.js\"></script>\n<script async src=\"https://javascript.info/article/script-async-defer/small.js\"></script>\n\n<p>...content after scripts...</p>\n```\n\n- The page content shows up immediately: `async` doesn't block it.\n- `DOMContentLoaded` may happen both before and after `async`, no guarantees here.\n- A smaller script `small.js` goes second, but probably loads before `long.js`, so `small.js` runs first. Although, it might be that `long.js` loads first, if cached, then it runs first. In other words, async scripts run in the \"load-first\" order.\n\nAsync scripts are great when we integrate an independent third-party script into the page: counters, ads and so on, as they don't depend on our scripts, and our scripts shouldn't wait for them:\n\n```html\n<!-- Google Analytics is usually added like this -->\n<script async src=\"https://google-analytics.com/analytics.js\"></script>\n```\n\n## Dynamic scripts\n \nThere's one more important way of adding a script to the page.\n\nWe can create a script and append it to the document dynamically using JavaScript:\n\n```js run\nlet script = document.createElement('script');\nscript.src = \"/article/script-async-defer/long.js\";\ndocument.body.append(script); // (*)\n```\n\nThe script starts loading as soon as it's appended to the document `(*)`.\n\n**Dynamic scripts behave as \"async\" by default.**\n\nThat is:\n- They don't wait for anything, nothing waits for them.\n- The script that loads first -- runs first (\"load-first\" order).\n\nThis can be changed if we explicitly set `script.async=false`. Then scripts will be executed in the document order, just like `defer`.\n\nIn this example, `loadScript(src)` function adds a script and also sets `async` to `false`.\n\nSo `long.js` always runs first (as it's added first):\n\n```js run\nfunction loadScript(src) {\n  let script = document.createElement('script');\n  script.src = src;\n  script.async = false;\n  document.body.append(script);\n}\n\n// long.js runs first because of async=false\nloadScript(\"/article/script-async-defer/long.js\");\nloadScript(\"/article/script-async-defer/small.js\");\n```\n\nWithout `script.async=false`, scripts would execute in default, load-first order (the `small.js` probably first).\n\nAgain, as with the `defer`, the order matters if we'd like to load a library and then another script that depends on it.\n\n\n## Summary\n\nBoth `async` and `defer` have one common thing: downloading of such scripts doesn't block page rendering. So the user can read page content and get acquainted with the page immediately.\n\nBut there are also essential differences between them:\n\n|         | Order | `DOMContentLoaded` |\n|---------|---------|---------|\n| `async` | *Load-first order*. Their document order doesn't matter -- which loads first runs first |  Irrelevant. May load and execute while the document has not yet been fully downloaded. That happens if scripts are small or cached, and the document is long enough. |\n| `defer` | *Document order* (as they go in the document). |  Execute after the document is loaded and parsed (they wait if needed), right before `DOMContentLoaded`. |\n\nIn practice, `defer` is used for scripts that need the whole DOM and/or their relative execution order is important. \n\nAnd  `async` is used for independent scripts, like counters or ads. And their relative execution order does not matter.\n\n```warn header=\"Page without scripts should be usable\"\nPlease note: if you're using `defer` or `async`, then user will see the the page *before* the script loads.\n\nIn such case, some graphical components are probably not initialized yet.\n\nDon't forget to put \"loading\" indication and disable buttons that aren't functional yet. Let the user clearly see what he can do on the page, and what's still getting ready.\n```\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/long.js",
    "content": "// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n// ...long js... ...long js... ...long js... ...long js... ...long js... ...long js...\n\nalert(\"Long script loaded\");\n"
  },
  {
    "path": "2-ui/5-loading/02-script-async-defer/small.js",
    "content": "// ...small js...\n\nalert(\"Small script loaded\");\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.md",
    "content": "\nThe algorithm:\n1. Make `img` for every source.\n2. Add `onload/onerror` for every image.\n3. Increase the counter when either `onload` or `onerror` triggers.\n4. When the counter value equals to the sources count -- we're done: `callback()`.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <script>\n    function preloadImages(sources, callback) {\n      let counter = 0;\n\n      function onLoad() {\n        counter++;\n        if (counter == sources.length) callback();\n      }\n\n      for(let source of sources) {\n        let img = document.createElement('img');\n        img.onload = img.onerror = onLoad;\n        img.src = source;\n      }\n    }\n\n    // ---------- The test ----------\n\n    let sources = [\n      \"https://en.js.cx/images-load/1.jpg\",\n      \"https://en.js.cx/images-load/2.jpg\",\n      \"https://en.js.cx/images-load/3.jpg\"\n    ];\n\n    // add random characters to prevent browser caching\n    for (let i = 0; i < sources.length; i++) {\n      sources[i] += '?' + Math.random();\n    }\n\n    // for each image,\n    // let's create another img with the same src and check that we have its width \n    function testLoaded() {\n      let widthSum = 0;\n      for (let i = 0; i < sources.length; i++) {\n        let img = document.createElement('img');\n        img.src = sources[i];\n        widthSum += img.width;\n      }\n      alert(widthSum);\n    }\n\n    // should output 300\n    preloadImages(sources, testLoaded);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <script>\n    function preloadImages(sources, callback) {\n      /* your code */\n    }\n\n    // ---------- The test ----------\n\n    let sources = [\n      \"https://en.js.cx/images-load/1.jpg\",\n      \"https://en.js.cx/images-load/2.jpg\",\n      \"https://en.js.cx/images-load/3.jpg\"\n    ];\n\n    // add random characters to prevent browser caching\n    for (let i = 0; i < sources.length; i++) {\n      sources[i] += '?' + Math.random();\n    }\n\n    // for each image,\n    // let's create another img with the same src and check that we have its width immediately\n    function testLoaded() {\n      let widthSum = 0;\n      for (let i = 0; i < sources.length; i++) {\n        let img = document.createElement('img');\n        img.src = sources[i];\n        widthSum += img.width;\n      }\n      alert(widthSum);\n    }\n\n    // every image is 100x100, the total width should be 300\n    preloadImages(sources, testLoaded);\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/1-load-img-callback/task.md",
    "content": "importance: 4\n\n---\n\n# Load images with a callback\n\nNormally, images are loaded when they are created. So when we add `<img>` to the page, the user does not see the picture immediately. The browser needs to load it first.\n\nTo show an image immediately, we can create it \"in advance\", like this:\n\n```js\nlet img = document.createElement('img');\nimg.src = 'my.jpg';\n```\n\nThe browser starts loading the image and remembers it in the cache. Later, when the same image appears in the document (no matter how), it shows up immediately.\n\n**Create a function `preloadImages(sources, callback)` that loads all images from the array `sources` and, when ready, runs `callback`.**\n\nFor instance, this will show an `alert` after the images are loaded:\n\n```js\nfunction loaded() {\n  alert(\"Images loaded\")\n}\n\npreloadImages([\"1.jpg\", \"2.jpg\", \"3.jpg\"], loaded);\n```\n\nIn case of an error, the function should still assume the picture \"loaded\".\n\nIn other words, the `callback` is executed when all images are either loaded or errored out.\n\nThe function is useful, for instance, when we plan to show a gallery with many scrollable images, and want to be sure that all images are loaded.\n\nIn the source document you can find links to test images, and also the code to check whether they are loaded or not. It should output `300`.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/article.md",
    "content": "# Resource loading: onload and onerror\n\nThe browser allows us to track the loading of external resources -- scripts, iframes, pictures and so on.\n\nThere are two events for it:\n\n- `onload` -- successful load,\n- `onerror` -- an error occurred.\n\n## Loading a script\n\nLet's say we need to load a third-party script and call a function that resides there.\n\nWe can load it dynamically, like this:\n\n```js\nlet script = document.createElement('script');\nscript.src = \"my.js\";\n\ndocument.head.append(script);\n```\n\n...But how to run the function that is declared inside that script? We need to wait until the script loads, and only then we can call it.\n\n```smart\nFor our own scripts we could use [JavaScript modules](info:modules) here, but they are not widely adopted by third-party libraries.\n```\n\n### script.onload\n\nThe main helper is the `load` event. It triggers after the script was loaded and executed.\n\nFor instance:\n\n```js run untrusted\nlet script = document.createElement('script');\n\n// can load any script, from any domain\nscript.src = \"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.3.0/lodash.js\"\ndocument.head.append(script);\n\n*!*\nscript.onload = function() {\n  // the script creates a variable \"_\"\n  alert( _.VERSION ); // shows library version\n};\n*/!*\n```\n\nSo in `onload` we can use script variables, run functions etc.\n\n...And what if the loading failed? For instance, there's no such script (error 404) or the server is down (unavailable).\n\n### script.onerror\n\nErrors that occur during the loading of the script can be tracked in an `error` event.\n\nFor instance, let's request a script that doesn't exist:\n\n```js run\nlet script = document.createElement('script');\nscript.src = \"https://example.com/404.js\"; // no such script\ndocument.head.append(script);\n\n*!*\nscript.onerror = function() {\n  alert(\"Error loading \" + this.src); // Error loading https://example.com/404.js\n};\n*/!*\n```\n\nPlease note that we can't get HTTP error details here. We don't know if it was an error 404 or 500 or something else. Just that the loading failed.\n\n```warn\nEvents `onload`/`onerror` track only the loading itself.\n\nErrors that may occur during script processing and execution are out of scope for these events. That is: if a script loaded successfully, then `onload` triggers, even if it has programming errors in it. To track script errors, one can use `window.onerror` global handler.\n```\n\n## Other resources\n\nThe `load` and `error` events also work for other resources, basically for any resource that has an external `src`.\n\nFor example:\n\n```js run\nlet img = document.createElement('img');\nimg.src = \"https://js.cx/clipart/train.gif\"; // (*)\n\nimg.onload = function() {\n  alert(`Image loaded, size ${img.width}x${img.height}`);\n};\n\nimg.onerror = function() {\n  alert(\"Error occurred while loading image\");\n};\n```\n\nThere are some notes though:\n\n- Most resources start loading when they are added to the document. But `<img>` is an exception. It starts loading when it gets a src `(*)`.\n- For `<iframe>`, the `iframe.onload` event triggers when the iframe loading finished, both for successful load and in case of an error.\n\nThat's for historical reasons.\n\n## Crossorigin policy\n\nThere's a rule: scripts from one site can't access contents of the other site. So, e.g. a script at `https://facebook.com` can't read the user's mailbox at `https://gmail.com`.\n\nOr, to be more precise, one origin (domain/port/protocol triplet) can't access the content from another one. So even if we have a subdomain, or just another port, these are different origins with no access to each other.\n\nThis rule also affects resources from other domains.\n\nIf we're using a script from another domain, and there's an error in it, we can't get error details.\n\nFor example, let's take a script `error.js` that consists of a single (bad) function call:\n```js\n// 📁 error.js\nnoSuchFunction();\n```\n\nNow load it from the same site where it's located:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script src=\"/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nWe can see a good error report, like this:\n\n```\nUncaught ReferenceError: noSuchFunction is not defined\nhttps://javascript.info/article/onload-onerror/crossorigin/error.js, 1:1\n```\n\nNow let's load the same script from another domain:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script src=\"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nThe report is different, like this:\n\n```\nScript error.\n, 0:0\n```\n\nDetails may vary depending on the browser, but the idea is the same: any information about the internals of a script, including error stack traces, is hidden. Exactly because it's from another domain.\n\nWhy do we need error details?\n\nThere are many services (and we can build our own) that listen for global errors using `window.onerror`, save errors and provide an interface to access and analyze them. That's great, as we can see real errors, triggered by our users. But if a script comes from another origin, then there's not much information about errors in it, as we've just seen.\n\nSimilar cross-origin policy (CORS) is enforced for other types of resources as well.\n\n**To allow cross-origin access, the `<script>` tag needs to have the `crossorigin` attribute, plus the remote server must provide special headers.**\n\nThere are three levels of cross-origin access:\n\n1. **No `crossorigin` attribute** -- access prohibited.\n2. **`crossorigin=\"anonymous\"`** -- access allowed if the server responds with the header `Access-Control-Allow-Origin` with `*` or our origin. Browser does not send authorization information and cookies to remote server.\n3. **`crossorigin=\"use-credentials\"`** -- access allowed if the server sends back the header `Access-Control-Allow-Origin` with our origin and `Access-Control-Allow-Credentials: true`. Browser sends authorization information and cookies to remote server.\n\n```smart\nYou can read more about cross-origin access in the chapter <info:fetch-crossorigin>. It describes the `fetch` method for network requests, but the policy is exactly the same.\n\nSuch thing as \"cookies\" is out of our current scope, but you can read about them in the chapter <info:cookie>.\n```\n\nIn our case, we didn't have any crossorigin attribute. So the cross-origin access was prohibited. Let's add it.\n\nWe can choose between `\"anonymous\"` (no cookies sent, one server-side header needed) and `\"use-credentials\"` (sends cookies too, two server-side headers needed).\n\nIf we don't care about cookies, then `\"anonymous\"` is the way to go:\n\n```html run height=0\n<script>\nwindow.onerror = function(message, url, line, col, errorObj) {\n  alert(`${message}\\n${url}, ${line}:${col}`);\n};\n</script>\n<script *!*crossorigin=\"anonymous\"*/!* src=\"https://cors.javascript.info/article/onload-onerror/crossorigin/error.js\"></script>\n```\n\nNow, assuming that the server provides an `Access-Control-Allow-Origin` header, everything's fine. We have the full error report.\n\n## Summary\n\nImages `<img>`, external styles, scripts and other resources provide `load` and `error` events to track their loading:\n\n- `load` triggers on a successful load,\n- `error` triggers on a failed load.\n\nThe only exception is `<iframe>`: for historical reasons it always triggers `load`, for any load completion, even if the page is not found.\n\nThe `readystatechange` event also works for resources, but is rarely used, because `load/error` events are simpler.\n"
  },
  {
    "path": "2-ui/5-loading/03-onload-onerror/crossorigin.view/error.js",
    "content": "noSuchFunction();\n"
  },
  {
    "path": "2-ui/5-loading/index.md",
    "content": "\n# Document and resource loading\n"
  },
  {
    "path": "2-ui/99-ui-misc/01-mutation-observer/article.md",
    "content": "\n# Mutation observer\n\n`MutationObserver` is a built-in object that observes a DOM element and fires a callback when it detects a change.\n\nWe'll first take a look at the syntax, and then explore a real-world use case, to see where such thing may be useful.\n\n## Syntax\n\n`MutationObserver` is easy to use.\n\nFirst, we create an observer with a callback-function:\n\n```js\nlet observer = new MutationObserver(callback);\n```\n\nAnd then attach it to a DOM node:\n\n```js\nobserver.observe(node, config);\n```\n\n`config` is an object with boolean options \"what kind of changes to react on\":\n- `childList` -- changes in the direct children of `node`,\n- `subtree` -- in all descendants of `node`,\n- `attributes` -- attributes of `node`,\n- `attributeFilter` -- an array of attribute names, to observe only selected ones.\n- `characterData` -- whether to observe `node.data` (text content),\n\nFew other options:\n- `attributeOldValue` -- if `true`, pass both the old and the new value of attribute to callback (see below), otherwise only the new one (needs `attributes` option),\n- `characterDataOldValue` -- if `true`, pass both the old and the new value of `node.data` to callback (see below), otherwise only the new one (needs `characterData` option).\n\nThen after any changes, the `callback` is executed: changes are passed in the first argument as a list of [MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects, and the observer itself as the second argument.\n\n[MutationRecord](https://dom.spec.whatwg.org/#mutationrecord) objects have properties:\n\n- `type` -- mutation type, one of\n    - `\"attributes\"`: attribute modified\n    - `\"characterData\"`: data modified, used for text nodes,\n    - `\"childList\"`: child elements added/removed,\n- `target` -- where the change occurred: an element for `\"attributes\"`, or text node for `\"characterData\"`, or an element for a `\"childList\"` mutation,\n- `addedNodes/removedNodes`  -- nodes that were added/removed,\n- `previousSibling/nextSibling` -- the previous and next sibling to added/removed nodes,\n- `attributeName/attributeNamespace` -- the name/namespace (for XML) of the changed attribute,\n- `oldValue` -- the previous value, only for attribute or text changes, if the corresponding option is set `attributeOldValue`/`characterDataOldValue`.\n\nFor example, here's a `<div>` with a `contentEditable` attribute. That attribute allows us to focus on it and edit.\n\n```html run\n<div contentEditable id=\"elem\">Click and <b>edit</b>, please</div>\n\n<script>\nlet observer = new MutationObserver(mutationRecords => {\n  console.log(mutationRecords); // console.log(the changes)\n});\n\n// observe everything except attributes\nobserver.observe(elem, {\n  childList: true, // observe direct children\n  subtree: true, // and lower descendants too\n  characterDataOldValue: true // pass old data to callback\n});\n</script>\n```\n\nIf we run this code in the browser, then focus on the given `<div>` and change the text inside `<b>edit</b>`, `console.log` will show one mutation:\n\n```js\nmutationRecords = [{\n  type: \"characterData\",\n  oldValue: \"edit\",\n  target: <text node>,\n  // other properties empty\n}];\n```\n\nIf we make more complex editing operations, e.g. remove the `<b>edit</b>`, the mutation event may contain multiple mutation records:\n\n```js\nmutationRecords = [{\n  type: \"childList\",\n  target: <div#elem>,\n  removedNodes: [<b>],\n  nextSibling: <text node>,\n  previousSibling: <text node>\n  // other properties empty\n}, {\n  type: \"characterData\"\n  target: <text node>\n  // ...mutation details depend on how the browser handles such removal\n  // it may coalesce two adjacent text nodes \"edit \" and \", please\" into one node\n  // or it may leave them separate text nodes\n}];\n```\n\nSo, `MutationObserver` allows to react on any changes within DOM subtree.\n\n## Usage for integration\n\nWhen such thing may be useful?\n\nImagine the situation when you need to add a third-party script that contains useful functionality, but also does something unwanted, e.g. shows ads `<div class=\"ads\">Unwanted ads</div>`.\n\nNaturally, the third-party script provides no mechanisms to remove it.\n\nUsing `MutationObserver`, we can detect when the unwanted element appears in our DOM and remove it.\n\nThere are other situations when a third-party script adds something into our document, and we'd like to detect, when it happens, to adapt our page, dynamically resize something etc.\n\n`MutationObserver` allows to implement this.\n\n## Usage for architecture\n\nThere are also situations when `MutationObserver` is good from architectural standpoint.\n\nLet's say we're making a website about programming. Naturally, articles and other materials may contain source code snippets.\n\nSuch snippet in an HTML markup looks like this:\n\n```html\n...\n<pre class=\"language-javascript\"><code>\n  // here's the code\n  let hello = \"world\";\n</code></pre>\n...\n```\n\nFor better readability and at the same time, to beautify it, we'll be using a JavaScript syntax highlighting library on our site, like [Prism.js](https://prismjs.com/). To get syntax highlighting for above snippet in Prism, `Prism.highlightElem(pre)` is called, which examines the contents of such `pre` elements and adds special tags and styles for colored syntax highlighting into those elements, similar to what you see in examples here, on this page.\n\nWhen exactly should we run that highlighting method? Well, we can do it on `DOMContentLoaded` event, or put the script at the bottom of the page. The moment our DOM is ready, we can search for elements `pre[class*=\"language\"]` and call `Prism.highlightElem` on them:\n\n```js\n// highlight all code snippets on the page\ndocument.querySelectorAll('pre[class*=\"language\"]').forEach(Prism.highlightElem);\n```\n\nEverything's simple so far, right? We find code snippets in HTML and highlight them.\n\nNow let's go on. Let's say we're going to dynamically fetch materials from a server. We'll study methods for that [later in the tutorial](info:fetch). For now it only matters that we fetch an HTML article from a webserver and display it on demand:\n\n```js\nlet article = /* fetch new content from server */\narticleElem.innerHTML = article;\n```\n\nThe new `article` HTML may contain code snippets. We need to call `Prism.highlightElem` on them, otherwise they won't get highlighted.\n\n**Where and when to call `Prism.highlightElem` for a dynamically loaded article?**\n\nWe could append that call to the code that loads an article, like this:\n\n```js\nlet article = /* fetch new content from server */\narticleElem.innerHTML = article;\n\n*!*\nlet snippets = articleElem.querySelectorAll('pre[class*=\"language-\"]');\nsnippets.forEach(Prism.highlightElem);\n*/!*\n```\n\n...But, imagine if we have many places in the code where we load our content - articles, quizzes, forum posts, etc. Do we need to put the highlighting call everywhere, to highlight the code in content after loading? That's not very convenient.\n\nAnd what if the content is loaded by a third-party module? For example, we have a forum written by someone else, that loads content dynamically, and we'd like to add syntax highlighting to it. No one likes patching third-party scripts.\n\nLuckily, there's another option.\n\nWe can use `MutationObserver` to automatically detect when code snippets are inserted into the page and highlight them.\n\nSo we'll handle the highlighting functionality in one place, relieving us from the need to integrate it.\n\n### Dynamic highlight demo\n\nHere's the working example.\n\nIf you run this code, it starts observing the element below and highlighting any code snippets that appear there:\n\n```js run\nlet observer = new MutationObserver(mutations => {\n\n  for(let mutation of mutations) {\n    // examine new nodes, is there anything to highlight?\n\n    for(let node of mutation.addedNodes) {\n      // we track only elements, skip other nodes (e.g. text nodes)\n      if (!(node instanceof HTMLElement)) continue;\n\n      // check the inserted element for being a code snippet\n      if (node.matches('pre[class*=\"language-\"]')) {\n        Prism.highlightElement(node);\n      }\n\n      // or maybe there's a code snippet somewhere in its subtree?\n      for(let elem of node.querySelectorAll('pre[class*=\"language-\"]')) {\n        Prism.highlightElement(elem);\n      }\n    }\n  }\n\n});\n\nlet demoElem = document.getElementById('highlight-demo');\n\nobserver.observe(demoElem, {childList: true, subtree: true});\n```\n\nHere, below, there's an HTML-element and JavaScript that dynamically fills it using `innerHTML`.\n\nPlease run the previous code (above, observes that element), and then the code below. You'll see how `MutationObserver` detects and highlights the snippet.\n\n<p id=\"highlight-demo\" style=\"border: 1px solid #ddd\">A demo-element with <code>id=\"highlight-demo\"</code>, run the code above to observe it.</p>\n\nThe following code populates its `innerHTML`, that causes the `MutationObserver` to react and highlight its contents:\n\n```js run\nlet demoElem = document.getElementById('highlight-demo');\n\n// dynamically insert content with code snippets\ndemoElem.innerHTML = `A code snippet is below:\n  <pre class=\"language-javascript\"><code> let hello = \"world!\"; </code></pre>\n  <div>Another one:</div>\n  <div>\n    <pre class=\"language-css\"><code>.class { margin: 5px; } </code></pre>\n  </div>\n`;\n```\n\nNow we have `MutationObserver` that can track all highlighting in observed elements or the whole `document`. We can add/remove code snippets in HTML without thinking about it.\n\n## Additional methods\n\nThere's a method to stop observing the node:\n\n- `observer.disconnect()` -- stops the observation.\n\nWhen we stop the observing, it might be possible that some changes were not yet processed by the observer. In such cases, we use\n\n- `observer.takeRecords()` -- gets a list of unprocessed mutation records - those that happened, but the callback has not handled them.\n\nThese methods can be used together, like this:\n\n```js\n// get a list of unprocessed mutations\n// should be called before disconnecting,\n// if you care about possibly unhandled recent mutations\nlet mutationRecords = observer.takeRecords();\n\n// stop tracking changes\nobserver.disconnect();\n...\n```\n\n\n```smart header=\"Records returned by `observer.takeRecords()` are removed from the processing queue\"\nThe callback won't be called for records, returned by `observer.takeRecords()`.\n```\n\n```smart header=\"Garbage collection interaction\"\nObservers use weak references to nodes internally. That is, if a node is removed from the DOM, and becomes unreachable, then it can be garbage collected.\n\nThe mere fact that a DOM node is observed doesn't prevent the garbage collection.\n```\n\n## Summary  \n\n`MutationObserver` can react to changes in DOM - attributes, text content and adding/removing elements.\n\nWe can use it to track changes introduced by other parts of our code, as well as to integrate with third-party scripts.\n\n`MutationObserver` can track any changes. The config \"what to observe\" options are used for optimizations, not to spend resources on unneeded callback invocations.\n"
  },
  {
    "path": "2-ui/99-ui-misc/02-selection-range/article.md",
    "content": "libs:\n  - d3\n  - domtree\n\n---\n\n# Selection and Range\n\nIn this chapter we'll cover selection in the document, as well as selection in form fields, such as `<input>`.\n\nJavaScript can access an existing selection, select/deselect DOM nodes as a whole or partially, remove the selected content from the document, wrap it into a tag, and so on.\n\nYou can find some recipes for common tasks at the end of the chapter, in \"Summary\" section. Maybe that covers your current needs, but you'll get much more if you read the whole text.\n\nThe underlying `Range` and `Selection` objects are easy to grasp, and then you'll need no recipes to make them do what you want.\n\n## Range\n\nThe basic concept of selection is [Range](https://dom.spec.whatwg.org/#ranges), that is essentially a pair of \"boundary points\": range start and range end.\n\nA `Range` object is created without parameters:\n\n```js\nlet range = new Range();\n```\n\nThen we can set the selection boundaries using `range.setStart(node, offset)` and `range.setEnd(node, offset)`.\n\nAs you might guess, further we'll use the `Range` objects for selection, but first let's create few such objects.\n\n### Selecting the text partially\n\nThe interesting thing is that the first argument `node` in both methods can be either a text node or an element node, and the meaning of the second argument depends on that.\n\n**If `node` is a text node, then `offset` must be the position in its text.**\n\nFor example, given the element `<p>Hello</p>`, we can create the range containing the letters \"ll\" as follows:\n\n```html run\n<p id=\"p\">Hello</p>\n<script>\n  let range = new Range();\n  range.setStart(p.firstChild, 2);\n  range.setEnd(p.firstChild, 4);\n  \n  // toString of a range returns its content as text\n  console.log(range); // ll\n</script>\n```\n\nHere we take the first child of `<p>` (that's the text node) and specify the text positions inside it:\n\n![](range-hello-1.svg)\n\n### Selecting element nodes\n\n**Alternatively, if `node` is an element node, then `offset` must be the child number.** \n\nThat's handy for making ranges that contain nodes as a whole, not stop somewhere inside their text.\n\nFor example, we have a more complex document fragment:\n\n```html autorun\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n```\n\nHere's its DOM structure with both element and text nodes:\n\n<div class=\"select-p-domtree\"></div>\n\n<script>\nlet selectPDomtree = {\n  \"name\": \"P\",\n  \"nodeType\": 1,\n  \"children\": [{\n    \"name\": \"#text\",\n    \"nodeType\": 3,\n    \"content\": \"Example: \"\n  }, {\n    \"name\": \"I\",\n    \"nodeType\": 1,\n    \"children\": [{\n      \"name\": \"#text\",\n      \"nodeType\": 3,\n      \"content\": \"italic\"\n    }]\n  }, {\n    \"name\": \"#text\",\n    \"nodeType\": 3,\n    \"content\": \" and \"\n  }, {\n    \"name\": \"B\",\n    \"nodeType\": 1,\n    \"children\": [{\n      \"name\": \"#text\",\n      \"nodeType\": 3,\n      \"content\": \"bold\"\n    }]\n  }]\n}\n\ndrawHtmlTree(selectPDomtree, 'div.select-p-domtree', 690, 320);\n</script>\n\nLet's make a range for `\"Example: <i>italic</i>\"`.\n\nAs we can see, this phrase consists of exactly two children of `<p>`, with indexes `0` and `1`:\n\n![](range-example-p-0-1.svg)\n\n- The starting point has `<p>` as the parent `node`, and `0` as the offset.\n\n    So we can set it as `range.setStart(p, 0)`.\n- The ending point also has `<p>` as the parent `node`, but `2` as the offset (it specifies the range up to, but not including `offset`).\n\n    So we can set it as `range.setEnd(p, 2)`.\n\nHere's the demo. If you run it, you can see that the text gets selected:\n\n```html run\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<script>\n*!*\n  let range = new Range();\n\n  range.setStart(p, 0);\n  range.setEnd(p, 2);\n*/!*\n\n  // toString of a range returns its content as text, without tags\n  console.log(range); // Example: italic\n\n  // apply this range for document selection (explained later below)\n  document.getSelection().addRange(range);\n</script>\n```\n\nHere's a more flexible test stand where you can set range start/end numbers and explore other variants:\n\n```html run autorun\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\nFrom <input id=\"start\" type=\"number\" value=1> – To <input id=\"end\" type=\"number\" value=4>\n<button id=\"button\">Click to select</button>\n<script>\n  button.onclick = () => {\n  *!*\n    let range = new Range();\n\n    range.setStart(p, start.value);\n    range.setEnd(p, end.value);\n  */!*\n\n    // apply the selection, explained later below\n    document.getSelection().removeAllRanges();\n    document.getSelection().addRange(range);\n  };\n</script>\n```\n\nE.g. selecting in the same `<p>` from offset `1` to `4` gives us the range `<i>italic</i> and <b>bold</b>`:\n\n![](range-example-p-1-3.svg)\n\n```smart header=\"Starting and ending nodes can be different\"\nWe don't have to use the same node in `setStart` and `setEnd`. A range may span across many unrelated nodes. It's only important that the end is after the start in the document.\n```\n\n### Selecting a bigger fragment\n\nLet's make a bigger selection in our example, like this:\n\n![](range-example-p-2-b-3.svg)\n\nWe already know how to do that. We just need to set the start and the end as a relative offset in text nodes.\n\nWe need to create a range, that:\n- starts from position 2 in `<p>` first child (taking all but two first letters of \"Ex<b>ample:</b> \")\n- ends at the position 3 in `<b>` first child (taking first three letters of \"<b>bol</b>d\", but no more):\n\n```html run\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  let range = new Range();\n\n  range.setStart(p.firstChild, 2);\n  range.setEnd(p.querySelector('b').firstChild, 3);\n\n  console.log(range); // ample: italic and bol\n\n  // use this range for selection (explained later)\n  window.getSelection().addRange(range);\n</script>\n```\n\nAs you can see, it's fairly easy to make a range of whatever we want.\n\nIf we'd like to take nodes as a whole, we can pass elements in `setStart/setEnd`. Otherwise, we can work on the text level. \n\n## Range properties\n\nThe range object that we created in the example above has following properties:\n\n![](range-example-p-2-b-3-range.svg)\n\n- `startContainer`, `startOffset` -- node and offset of the start,\n  - in the example above: first text node inside `<p>` and `2`.\n- `endContainer`, `endOffset` -- node and offset of the end,\n  - in the example above: first text node inside `<b>` and `3`.\n- `collapsed` -- boolean, `true` if the range starts and ends on the same point (so there's no content inside the range),\n  - in the example above: `false`\n- `commonAncestorContainer` -- the nearest common ancestor of all nodes within the range,\n  - in the example above: `<p>`\n\n\n## Range selection methods\n\nThere are many convenience methods to manipulate ranges.\n\nWe've already seen `setStart` and `setEnd`, here are other similar methods.\n\nSet range start:\n\n- `setStart(node, offset)` set start at: position `offset` in `node`\n- `setStartBefore(node)` set start at: right before `node`\n- `setStartAfter(node)` set start at: right after `node`\n\nSet range end (similar methods):\n\n- `setEnd(node, offset)` set end at: position `offset` in `node`\n- `setEndBefore(node)` set end at: right before `node`\n- `setEndAfter(node)` set end at: right after `node`\n\nTechnically, `setStart/setEnd` can do anything, but more methods provide more convenience.\n\nIn all these methods, `node` can be both a text or element node: for text nodes `offset` skips that many of characters, while for element nodes that many child nodes.\n\nEven more methods to create ranges:\n- `selectNode(node)` set range to select the whole `node`\n- `selectNodeContents(node)` set range to select the whole `node` contents\n- `collapse(toStart)` if `toStart=true` set end=start, otherwise set start=end, thus collapsing the range\n- `cloneRange()` creates a new range with the same start/end\n\n## Range editing methods\n\nOnce the range is created, we can manipulate its content using these methods:\n\n- `deleteContents()` -- remove range content from the document\n- `extractContents()` -- remove range content from the document and return as [DocumentFragment](info:modifying-document#document-fragment)\n- `cloneContents()` -- clone range content and return as [DocumentFragment](info:modifying-document#document-fragment)\n- `insertNode(node)` -- insert `node` into the document at the beginning of the range\n- `surroundContents(node)` -- wrap `node` around range content. For this to work, the range must contain both opening and closing tags for all elements inside it: no partial ranges like `<i>abc`.\n\nWith these methods we can do basically anything with selected nodes.\n\nHere's the test stand to see them in action:\n\n```html run refresh autorun height=260\nClick buttons to run methods on the selection, \"resetExample\" to reset it.\n\n<p id=\"p\">Example: <i>italic</i> and <b>bold</b></p>\n\n<p id=\"result\"></p>\n<script>\n  let range = new Range();\n\n  // Each demonstrated method is represented here:\n  let methods = {\n    deleteContents() {\n      range.deleteContents()\n    },\n    extractContents() {\n      let content = range.extractContents();\n      result.innerHTML = \"\";\n      result.append(\"extracted: \", content);\n    },\n    cloneContents() {\n      let content = range.cloneContents();\n      result.innerHTML = \"\";\n      result.append(\"cloned: \", content);\n    },\n    insertNode() {\n      let newNode = document.createElement('u');\n      newNode.innerHTML = \"NEW NODE\";\n      range.insertNode(newNode);\n    },\n    surroundContents() {\n      let newNode = document.createElement('u');\n      try {\n        range.surroundContents(newNode);\n      } catch(e) { console.log(e) }\n    },\n    resetExample() {\n      p.innerHTML = `Example: <i>italic</i> and <b>bold</b>`;\n      result.innerHTML = \"\";\n\n      range.setStart(p.firstChild, 2);\n      range.setEnd(p.querySelector('b').firstChild, 3);\n\n      window.getSelection().removeAllRanges();  \n      window.getSelection().addRange(range);  \n    }\n  };\n\n  for(let method in methods) {\n    document.write(`<div><button onclick=\"methods.${method}()\">${method}</button></div>`);\n  }\n\n  methods.resetExample();\n</script>\n```\n\nThere also exist methods to compare ranges, but these are rarely used. When you need them, please refer to the [spec](https://dom.spec.whatwg.org/#interface-range) or [MDN manual](mdn:/api/Range).\n\n\n## Selection\n\n`Range` is a generic object for managing selection ranges. Although, creating a `Range` doesn't mean that we see a selection on screen.\n\nWe may create `Range` objects, pass them around -- they do not visually select anything on their own.\n\nThe document selection is represented by `Selection` object, that can be obtained as `window.getSelection()` or `document.getSelection()`. A selection may include zero or more ranges. At least, the [Selection API specification](https://www.w3.org/TR/selection-api/) says so. In practice though, only Firefox allows to select multiple ranges in the document by using `key:Ctrl+click` (`key:Cmd+click` for Mac).\n\nHere's a screenshot of a selection with 3 ranges, made in Firefox:\n\n![](selection-firefox.svg)\n\nOther browsers support at maximum 1 range. As we'll see, some of `Selection` methods imply that there may be many ranges, but again, in all browsers except Firefox, there's at maximum 1.\n\nHere's a small demo that shows the current selection (select something and click) as text:\n\n<button onclick=\"alert(document.getSelection())\">alert(document.getSelection())</button>\n\n## Selection properties\n\nAs said, a selection may in theory contain multiple ranges. We can get these range objects using the method:\n\n- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used.\n\nAlso, there exist properties that often provide better convenience.\n\nSimilar to a range, a selection object has a start, called \"anchor\", and the end, called \"focus\".\n\nThe main selection properties are:\n\n- `anchorNode` -- the node where the selection starts,\n- `anchorOffset` -- the offset in `anchorNode` where the selection starts,\n- `focusNode` -- the node where the selection ends,\n- `focusOffset` -- the offset in `focusNode` where the selection ends,\n- `isCollapsed` -- `true` if selection selects nothing (empty range), or doesn't exist.\n- `rangeCount` -- count of ranges in the selection, maximum `1` in all browsers except Firefox.\n\n```smart header=\"Selection end/start vs Range\"\n\nThere's an important differences of a selection anchor/focus compared with a `Range` start/end.\n\nAs we know, `Range` objects always have their start before the end. \n\nFor selections, that's not always the case.\n\nSelecting something with a mouse can be done in both directions: either \"left-to-right\" or \"right-to-left\".\n\nIn other words, when the mouse button is pressed, and then it moves forward in the document, then its end (focus) will be after its start (anchor).\n\nE.g. if the user starts selecting with mouse and goes from \"Example\" to \"italic\":\n\n![](selection-direction-forward.svg)\n\n...But the same selection could be done backwards: starting from  \"italic\" to \"Example\" (backward direction), then its end (focus) will be before the start (anchor):\n\n![](selection-direction-backward.svg)\n```\n\n## Selection events\n\nThere are events on to keep track of selection:\n\n- `elem.onselectstart` -- when a selection *starts* specifically on element `elem` (or inside it). For instance, when the user presses the mouse button on it and starts to move the pointer.\n    - Preventing the default action cancels the selection start. So starting a selection from this element becomes impossible, but the element is still selectable. The visitor just needs to start the selection from elsewhere.\n- `document.onselectionchange` -- whenever a selection changes or starts.\n    - Please note: this handler can be set only on `document`, it tracks all selections in it.\n\n### Selection tracking demo\n\nHere's a small demo. It tracks the current selection on the `document` and shows its boundaries:\n\n```html run height=80\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\nFrom <input id=\"from\" disabled> – To <input id=\"to\" disabled>\n<script>\n  document.onselectionchange = function() {\n    let selection = document.getSelection();\n\n    let {anchorNode, anchorOffset, focusNode, focusOffset} = selection;\n\n    // anchorNode and focusNode are text nodes usually\n    from.value = `${anchorNode?.data}, offset ${anchorOffset}`;\n    to.value = `${focusNode?.data}, offset ${focusOffset}`;\n  };\n</script>\n```\n\n### Selection copying demo\n\nThere are two approaches to copying the selected content:\n\n1. We can use `document.getSelection().toString()` to get it as text.\n2. Otherwise, to copy the full DOM, e.g. if we need to keep formatting, we can get the underlying ranges with `getRangesAt(...)`. A `Range` object, in turn, has `cloneContents()` method that clones its content and returns as `DocumentFragment` object, that we can insert elsewhere.\n\nHere's the demo of copying the selected content both as text and as DOM nodes:\n\n```html run height=100\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\nCloned: <span id=\"cloned\"></span>\n<br>\nAs text: <span id=\"astext\"></span>\n\n<script>\n  document.onselectionchange = function() {\n    let selection = document.getSelection();\n\n    cloned.innerHTML = astext.innerHTML = \"\";\n\n    // Clone DOM nodes from ranges (we support multiselect here)\n    for (let i = 0; i < selection.rangeCount; i++) {\n      cloned.append(selection.getRangeAt(i).cloneContents());\n    }\n\n    // Get as text\n    astext.innerHTML += selection;\n  };\n</script>\n```\n\n## Selection methods\n\nWe can work with the selection by addding/removing ranges:\n\n- `getRangeAt(i)` -- get i-th range, starting from `0`. In all browsers except Firefox, only `0` is used.\n- `addRange(range)` -- add `range` to selection. All browsers except Firefox ignore the call, if the selection already has an associated range.\n- `removeRange(range)` -- remove `range` from the selection.\n- `removeAllRanges()` -- remove all ranges.\n- `empty()` -- alias to `removeAllRanges`.\n\nThere are also convenience methods to manipulate the selection range directly, without intermediate `Range` calls:\n\n- `collapse(node, offset)` -- replace selected range with a new one that starts and ends at the given `node`, at position `offset`.\n- `setPosition(node, offset)` -- alias to `collapse`.\n- `collapseToStart()` - collapse (replace with an empty range) to selection start,\n- `collapseToEnd()` - collapse to selection end,\n- `extend(node, offset)` - move focus of the selection to the given `node`, position `offset`,\n- `setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset)` - replace selection range with the given start `anchorNode/anchorOffset` and end `focusNode/focusOffset`. All content in-between them is selected.\n- `selectAllChildren(node)` -- select all children of the `node`.\n- `deleteFromDocument()` -- remove selected content from the document.\n- `containsNode(node, allowPartialContainment = false)` -- checks whether the selection contains `node` (partially if the second argument is `true`)\n\nFor most tasks these methods are just fine, there's no need to access the underlying `Range` object.\n\nFor example, selecting the whole contents of the paragraph `<p>`:\n\n```html run\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  // select from 0th child of <p> to the last child\n  document.getSelection().setBaseAndExtent(p, 0, p, p.childNodes.length);\n</script>\n```\n\nThe same thing using ranges:\n\n```html run\n<p id=\"p\">Select me: <i>italic</i> and <b>bold</b></p>\n\n<script>\n  let range = new Range();\n  range.selectNodeContents(p); // or selectNode(p) to select the <p> tag too\n\n  document.getSelection().removeAllRanges(); // clear existing selection if any\n  document.getSelection().addRange(range);\n</script>\n```\n\n```smart header=\"To select something, remove the existing selection first\"\nIf a document selection already exists, empty it first with `removeAllRanges()`. And then add ranges. Otherwise, all browsers except Firefox ignore new ranges.\n\nThe exception is some selection methods, that replace the existing selection, such as `setBaseAndExtent`.\n```\n\n## Selection in form controls\n\nForm elements, such as `input` and `textarea` provide [special API for selection](https://html.spec.whatwg.org/#textFieldSelection), without `Selection` or `Range` objects. As an input value is a pure text, not HTML, there's no need for such objects, everything's much simpler.\n\nProperties:\n- `input.selectionStart` -- position of selection start (writeable),\n- `input.selectionEnd` -- position of selection end (writeable),\n- `input.selectionDirection` -- selection direction, one of: \"forward\", \"backward\" or \"none\" (if e.g. selected with a double mouse click),\n\nEvents:\n- `input.onselect` -- triggers when something is selected.\n\nMethods:\n\n- `input.select()` -- selects everything in the text control (can be `textarea` instead of `input`),\n- `input.setSelectionRange(start, end, [direction])` -- change the selection to span from position `start` till `end`, in the given direction (optional).\n- `input.setRangeText(replacement, [start], [end], [selectionMode])` -- replace a range of text with the new text.\n\n    Optional arguments `start` and `end`, if provided, set the range start and end, otherwise user selection is used.\n\n    The last argument, `selectionMode`, determines how the selection will be set after the text has been replaced. The possible values are:\n\n    - `\"select\"` -- the newly inserted text will be selected.\n    - `\"start\"` -- the selection range collapses just before the inserted text (the cursor will be immediately before it).\n    - `\"end\"` -- the selection range collapses just after the inserted text (the cursor will be right after it).\n    - `\"preserve\"` -- attempts to preserve the selection. This is the default.\n\nNow let's see these methods in action.\n\n### Example: tracking selection\n\nFor example, this code uses `onselect` event to track selection:\n\n```html run autorun\n<textarea id=\"area\" style=\"width:80%;height:60px\">\nSelecting in this text updates values below.\n</textarea>\n<br>\nFrom <input id=\"from\" disabled> – To <input id=\"to\" disabled>\n\n<script>\n  area.onselect = function() {\n    from.value = area.selectionStart;\n    to.value = area.selectionEnd;\n  };\n</script>\n```\n\nPlease note:\n- `onselect` triggers when something is selected, but not when the selection is removed.\n- `document.onselectionchange` event should not trigger for selections inside a form control, according to the [spec](https://w3c.github.io/selection-api/#dfn-selectionchange), as it's not related to `document` selection and ranges. Some browsers generate it, but we shouldn't rely on it.\n\n\n### Example: moving cursor\n\nWe can change `selectionStart` and `selectionEnd`, that sets the selection.\n\nAn important edge case is when `selectionStart` and `selectionEnd` equal each other. Then it's exactly the cursor position. Or, to rephrase, when nothing is selected, the selection is collapsed at the cursor position.\n\nSo, by setting `selectionStart` and `selectionEnd` to the same value, we move the cursor.\n\nFor example:\n\n```html run autorun\n<textarea id=\"area\" style=\"width:80%;height:60px\">\nFocus on me, the cursor will be at position 10.\n</textarea>\n\n<script>\n  area.onfocus = () => {\n    // zero delay setTimeout to run after browser \"focus\" action finishes\n    setTimeout(() => {\n      // we can set any selection\n      // if start=end, the cursor is exactly at that place\n      area.selectionStart = area.selectionEnd = 10;\n    });\n  };\n</script>\n```\n\n### Example: modifying selection\n\nTo modify the content of the selection, we can use `input.setRangeText()` method. Of course, we can read `selectionStart/End` and, with the knowledge of the selection, change the corresponding substring of `value`, but `setRangeText` is more powerful and often more convenient.\n\nThat's a somewhat complex method. In its simplest one-argument form it replaces the user selected range and removes the selection.\n\nFor example, here the user selection will be wrapped by `*...*`:\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Select here and click the button\">\n<button id=\"button\">Wrap selection in stars *...*</button>\n\n<script>\nbutton.onclick = () => {\n  if (input.selectionStart == input.selectionEnd) {\n    return; // nothing is selected\n  }\n\n  let selected = input.value.slice(input.selectionStart, input.selectionEnd);\n  input.setRangeText(`*${selected}*`);\n};\n</script>\n```\n\nWith more arguments, we can set range `start` and `end`.\n\nIn this example we find `\"THIS\"` in the input text, replace it and keep the replacement selected:\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Replace THIS in text\">\n<button id=\"button\">Replace THIS</button>\n\n<script>\nbutton.onclick = () => {\n  let pos = input.value.indexOf(\"THIS\");\n  if (pos >= 0) {\n    input.setRangeText(\"*THIS*\", pos, pos + 4, \"select\");\n    input.focus(); // focus to make selection visible\n  }\n};\n</script>\n```\n\n### Example: insert at cursor\n\nIf nothing is selected, or we use equal `start` and `end` in `setRangeText`, then the new text is just inserted, nothing is removed.\n\nWe can also insert something \"at the cursor\" using `setRangeText`.\n\nHere's a button that inserts `\"HELLO\"` at the cursor position and puts the cursor immediately after it. If the selection is not empty, then it gets replaced (we can detect it by comparing `selectionStart!=selectionEnd` and do something else instead):\n\n```html run autorun\n<input id=\"input\" style=\"width:200px\" value=\"Text Text Text Text Text\">\n<button id=\"button\">Insert \"HELLO\" at cursor</button>\n\n<script>\n  button.onclick = () => {\n    input.setRangeText(\"HELLO\", input.selectionStart, input.selectionEnd, \"end\");\n    input.focus();\n  };    \n</script>\n```\n\n\n## Making unselectable\n\nTo make something unselectable, there are three ways:\n\n1. Use CSS property `user-select: none`.\n\n    ```html run\n    <style>\n    #elem {\n      user-select: none;\n    }\n    </style>\n    <div>Selectable <div id=\"elem\">Unselectable</div> Selectable</div>\n    ```\n\n    This doesn't allow the selection to start at `elem`. But the user may start the selection elsewhere and include `elem` into it.\n\n    Then `elem` will become a part of `document.getSelection()`, so the selection actually happens, but its content is usually ignored in copy-paste.\n\n\n2. Prevent default action in `onselectstart` or `mousedown` events.\n\n    ```html run\n    <div>Selectable <div id=\"elem\">Unselectable</div> Selectable</div>\n\n    <script>\n      elem.onselectstart = () => false;\n    </script>\n    ```\n\n    This prevents starting the selection on `elem`, but the visitor may start it at another element, then extend to `elem`.\n\n    That's convenient when there's another event handler on the same action that triggers the select (e.g. `mousedown`). So we disable the selection to avoid conflict, still allowing `elem` contents to be copied.\n\n3. We can also clear the selection post-factum after it happens with `document.getSelection().empty()`. That's rarely used, as this causes unwanted blinking as the selection appears-disappears.\n\n## References\n\n- [DOM spec: Range](https://dom.spec.whatwg.org/#ranges)\n- [Selection API](https://www.w3.org/TR/selection-api/#dom-globaleventhandlers-onselectstart)\n- [HTML spec: APIs for the text control selections](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#textFieldSelection)\n\n\n## Summary\n\nWe covered two different APIs for selections:\n\n1. For document: `Selection` and `Range` objects.\n2. For `input`, `textarea`: additional methods and properties.\n\nThe second API is very simple, as it works with text.\n\nThe most used recipes are probably:\n\n1. Getting the selection:\n    ```js\n    let selection = document.getSelection();\n\n    let cloned = /* element to clone the selected nodes to */;\n\n    // then apply Range methods to selection.getRangeAt(0)\n    // or, like here, to all ranges to support multi-select\n    for (let i = 0; i < selection.rangeCount; i++) {\n      cloned.append(selection.getRangeAt(i).cloneContents());\n    }\n    ```\n2. Setting the selection:\n    ```js\n    let selection = document.getSelection();\n\n    // directly:\n    selection.setBaseAndExtent(...from...to...);\n\n    // or we can create a range and:\n    selection.removeAllRanges();\n    selection.addRange(range);\n    ```\n\nAnd finally, about the cursor. The cursor position in editable elements, like `<textarea>` is always at the start or the end of the selection. We can use it  to get cursor position or to move the cursor by setting `elem.selectionStart` and `elem.selectionEnd`.\n"
  },
  {
    "path": "2-ui/99-ui-misc/03-event-loop/article.md",
    "content": "\n# Event loop: microtasks and macrotasks\n\nBrowser JavaScript execution flow, as well as in Node.js, is based on an *event loop*.\n\nUnderstanding how event loop works is important for optimizations, and sometimes for the right architecture.\n\nIn this chapter we first cover theoretical details about how things work, and then see practical applications of that knowledge.\n\n## Event Loop\n\nThe *event loop* concept is very simple. There's an endless loop, where the JavaScript engine waits for tasks, executes them and then sleeps, waiting for more tasks.\n\nThe general algorithm of the engine:\n\n1. While there are tasks:\n    - execute them, starting with the oldest task.\n2. Sleep until a task appears, then go to 1.\n\nThat's a formalization for what we see when browsing a page. The JavaScript engine does nothing most of the time, it only runs if a script/handler/event activates.\n\nExamples of tasks:\n\n- When an external script `<script src=\"...\">` loads, the task is to execute it.\n- When a user moves their mouse, the task is to dispatch `mousemove` event and execute handlers.\n- When the time is due for a scheduled `setTimeout`, the task is to run its callback.\n- ...and so on.\n\nTasks are set -- the engine handles them -- then waits for more tasks (while sleeping and consuming close to zero CPU).\n\nIt may happen that a task comes while the engine is busy, then it's enqueued.\n\nThe tasks form a queue, so-called \"macrotask queue\" (v8 term):\n\n![](eventLoop.svg)\n\nFor instance, while the engine is busy executing a `script`, a user may move their mouse causing `mousemove`, and `setTimeout` may be due and so on, these tasks form a queue, as illustrated on the picture above.\n\nTasks from the queue are processed on \"first come – first served\" basis. When the engine browser is done with the `script`, it handles `mousemove` event, then `setTimeout` handler, and so on.\n\nSo far, quite simple, right?\n\nTwo more details:\n1. Rendering never happens while the engine executes a task. It doesn't matter if the task takes a long time. Changes to the DOM are painted only after the task is complete.\n2. If a task takes too long, the browser can't do other tasks, such as processing user events. So after a time, it raises an alert like \"Page Unresponsive\", suggesting killing the task with the whole page. That happens when there are a lot of complex calculations or a programming error leading to an infinite loop.\n\nThat was the theory. Now let's see how we can apply that knowledge.\n\n## Use-case 1: splitting CPU-hungry tasks\n\nLet's say we have a CPU-hungry task.\n\nFor example, syntax-highlighting (used to colorize code examples on this page) is quite CPU-heavy. To highlight the code, it performs the analysis, creates many colored elements, adds them to the document -- for a large amount of text that takes a lot of time.\n\nWhile the engine is busy with syntax highlighting, it can't do other DOM-related stuff, process user events, etc. It may even cause the browser to \"hiccup\" or even \"hang\" for a bit, which is unacceptable.\n\nWe can avoid problems by splitting the big task into pieces. Highlight first 100 lines, then schedule `setTimeout` (with zero-delay) for the next 100 lines, and so on.\n\nTo demonstrate this approach, for the sake of simplicity, instead of text-highlighting, let's take a function that counts from `1` to `1000000000`.\n\nIf you run the code below, the engine will \"hang\" for some time. For server-side JS that's clearly noticeable, and if you are running it in-browser, then try to click other buttons on the page -- you'll see that no other events get handled until the counting finishes.\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // do a heavy job\n  for (let j = 0; j < 1e9; j++) {\n    i++;\n  }\n\n  alert(\"Done in \" + (Date.now() - start) + 'ms');\n}\n\ncount();\n```\n\nThe browser may even show a \"the script takes too long\" warning.\n\nLet's split the job using nested `setTimeout` calls:\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // do a piece of the heavy job (*)\n  do {\n    i++;\n  } while (i % 1e6 != 0);\n\n  if (i == 1e9) {\n    alert(\"Done in \" + (Date.now() - start) + 'ms');\n  } else {\n    setTimeout(count); // schedule the new call (**)\n  }\n\n}\n\ncount();\n```\n\nNow the browser interface is fully functional during the \"counting\" process.\n\nA single run of `count` does a part of the job `(*)`, and then re-schedules itself `(**)` if needed:\n\n1. First run counts: `i=1...1000000`.\n2. Second run counts: `i=1000001..2000000`.\n3. ...and so on.\n\nNow, if a new side task (e.g. `onclick` event) appears while the engine is busy executing part 1, it gets queued and then executes when part 1 finished, before the next part. Periodic returns to the event loop between `count` executions provide just enough \"air\" for the JavaScript engine to do something else, to react to other user actions.\n\nThe notable thing is that both variants -- with and without splitting the job by `setTimeout` -- are comparable in speed. There's not much difference in the overall counting time.\n\nTo make them closer, let's make an improvement.\n\nWe'll move the scheduling to the beginning of the `count()`:\n\n```js run\nlet i = 0;\n\nlet start = Date.now();\n\nfunction count() {\n\n  // move the scheduling to the beginning\n  if (i < 1e9 - 1e6) {\n    setTimeout(count); // schedule the new call\n  }\n\n  do {\n    i++;\n  } while (i % 1e6 != 0);\n\n  if (i == 1e9) {\n    alert(\"Done in \" + (Date.now() - start) + 'ms');\n  }\n\n}\n\ncount();\n```\n\nNow when we start to `count()` and see that we'll need to `count()` more, we schedule that immediately, before doing the job.\n\nIf you run it, it's easy to notice that it takes significantly less time.\n\nWhy?  \n\nThat's simple: as you remember, there's the in-browser minimal delay of 4ms for many nested `setTimeout` calls. Even if we set `0`, it's `4ms` (or a bit more). So the earlier we schedule it - the faster it runs.\n\nFinally, we've split a CPU-hungry task into parts - now it doesn't block the user interface. And its overall execution time isn't much longer.\n\n## Use case 2: progress indication\n\nAnother benefit of splitting heavy tasks for browser scripts is that we can show progress indication.\n\nAs mentioned earlier, changes to DOM are painted only after the currently running task is completed, irrespective of how long it takes.\n\nOn one hand, that's great, because our function may create many elements, add them one-by-one to the document and change their styles -- the visitor won't see any \"intermediate\", unfinished state. An important thing, right?\n\nHere's the demo, the changes to `i` won't show up until the function finishes, so we'll see only the last value:\n\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n\n  function count() {\n    for (let i = 0; i < 1e6; i++) {\n      i++;\n      progress.innerHTML = i;\n    }\n  }\n\n  count();\n</script>\n```\n\n...But we also may want to show something during the task, e.g. a progress bar.\n\nIf we split the heavy task into pieces using `setTimeout`, then changes are painted out in-between them.\n\nThis looks prettier:\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n  let i = 0;\n\n  function count() {\n\n    // do a piece of the heavy job (*)\n    do {\n      i++;\n      progress.innerHTML = i;\n    } while (i % 1e3 != 0);\n\n    if (i < 1e7) {\n      setTimeout(count);\n    }\n\n  }\n\n  count();\n</script>\n```\n\nNow the `<div>` shows increasing values of `i`, a kind of a progress bar.\n\n\n## Use case 3: doing something after the event\n\nIn an event handler we may decide to postpone some actions until the event bubbled up and was handled on all levels. We can do that by wrapping the code in zero delay `setTimeout`.\n\nIn the chapter <info:dispatch-events> we saw an example: custom event `menu-open` is dispatched in `setTimeout`, so that it happens after the \"click\" event is fully handled.\n\n```js\nmenu.onclick = function() {\n  // ...\n\n  // create a custom event with the clicked menu item data\n  let customEvent = new CustomEvent(\"menu-open\", {\n    bubbles: true\n  });\n\n  // dispatch the custom event asynchronously\n  setTimeout(() => menu.dispatchEvent(customEvent));\n};\n```\n\n## Macrotasks and Microtasks\n\nAlong with *macrotasks*, described in this chapter, there are *microtasks*, mentioned in the chapter <info:microtask-queue>.\n\nMicrotasks come solely from our code. They are usually created by promises: an execution of `.then/catch/finally` handler becomes a microtask. Microtasks are used \"under the cover\" of `await` as well, as it's another form of promise handling.\n\nThere's also a special function `queueMicrotask(func)` that queues `func` for execution in the microtask queue.\n\n**Immediately after every *macrotask*, the engine executes all tasks from *microtask* queue, prior to running any other macrotasks or rendering or anything else.**\n\nFor instance, take a look:\n\n```js run\nsetTimeout(() => alert(\"timeout\"));\n\nPromise.resolve()\n  .then(() => alert(\"promise\"));\n\nalert(\"code\");\n```\n\nWhat's going to be the order here?\n\n1. `code` shows first, because it's a regular synchronous call.\n2. `promise` shows second, because `.then` passes through the microtask queue, and runs after the current code.\n3. `timeout` shows last, because it's a macrotask.\n\nThe richer event loop picture looks like this (order is from top to bottom, that is: the script first, then microtasks, rendering and so on):\n\n![](eventLoop-full.svg)\n\nAll microtasks are completed before any other event handling or rendering or any other macrotask takes place.\n\nThat's important, as it guarantees that the application environment is basically the same (no mouse coordinate changes, no new network data, etc) between microtasks.\n\nIf we'd like to execute a function asynchronously (after the current code), but before changes are rendered or new events handled, we can schedule it with `queueMicrotask`.\n\nHere's an example with \"counting progress bar\", similar to the one shown previously, but `queueMicrotask` is used instead of `setTimeout`. You can see that it renders at the very end. Just like the synchronous code:\n\n```html run\n<div id=\"progress\"></div>\n\n<script>\n  let i = 0;\n\n  function count() {\n\n    // do a piece of the heavy job (*)\n    do {\n      i++;\n      progress.innerHTML = i;\n    } while (i % 1e3 != 0);\n\n    if (i < 1e6) {\n  *!*\n      queueMicrotask(count);\n  */!*\n    }\n\n  }\n\n  count();\n</script>\n```\n\n## Summary\n\nA more detailed event loop algorithm (though still simplified compared to the [specification](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)):\n\n1. Dequeue and run the oldest task from the *macrotask* queue (e.g. \"script\").\n2. Execute all *microtasks*:\n    - While the microtask queue is not empty:\n        - Dequeue and run the oldest microtask.\n3. Render changes if any.\n4. If the macrotask queue is empty, wait till a macrotask appears.\n5. Go to step 1.\n\nTo schedule a new *macrotask*:\n- Use zero delayed `setTimeout(f)`.\n\nThat may be used to split a big calculation-heavy task into pieces, for the browser to be able to react to user events and show progress between them.\n\nAlso, used in event handlers to schedule an action after the event is fully handled (bubbling done).\n\nTo schedule a new *microtask*\n- Use `queueMicrotask(f)`.\n- Also promise handlers go through the microtask queue.\n\nThere's no UI or network event handling between microtasks: they run immediately one after another.\n\nSo one may want to `queueMicrotask` to execute a function asynchronously, but within the environment state.\n\n```smart header=\"Web Workers\"\nFor long heavy calculations that shouldn't block the event loop, we can use [Web Workers](https://html.spec.whatwg.org/multipage/workers.html).\n\nThat's a way to run code in another, parallel thread.\n\nWeb Workers can exchange messages with the main process, but they have their own variables, and their own event loop.\n\nWeb Workers do not have access to DOM, so they are useful, mainly, for calculations, to use multiple CPU cores simultaneously.\n```\n"
  },
  {
    "path": "2-ui/99-ui-misc/index.md",
    "content": "\n# Miscellaneous\n"
  },
  {
    "path": "2-ui/index.md",
    "content": "# Browser: Document, Events, Interfaces \n\nMempelajari cara mengelola halaman pada browser: menambahkan elemen, memanipulasi ukuran dan posisinya, membuat antarmuka secara dinamis, dan berinteraksi dengan pengunjung.\n"
  },
  {
    "path": "3-frames-and-windows/01-popup-windows/article.md",
    "content": "# Metode Popup dan window\n\nJendela popup adalah salah satu metode tertua untuk menampilkan tambahan dokumen kepada pengguna.\nSecara umum, kamu hanya menjalankan:\n```js\nwindow.open('https://javascript.info/')\n```\n\n...Dan akan terbuka jendela baru yang telah diberi URL, Kebanyakan peramban moderen akan membuka jendela baru bukannya jendela terpisah.\nPopup ada sejak jaman dahulu. Ide awalnya adalah untuk menampilkan konten lain tanpa menutup jendela utama. Saat ini, ada cara lain untuk melakukan hal tersebut: Kita bisa membuka konten secara dinamis dengan [fetch](info:fetch) dan menampilkannya di dalam sebuah `<div>` yang dihasilkan secara dinamis. Jadi popups adalah sesuatu yang tidak kita gunakan setiap hari.\n\nKemudian, popups itu rumit di perangkat seluler, karena tidak menampilkan beberapa jendela secara serempak.\n\nNamun, ada tugas dimana popups masih digunakan, misalnya untuk otorisasi OAuth (masuk dengan Google/Facebook/...), karena:\n1. Popup adalah sebuah jendela terpisah dengan ekosistem Javascript independennya sendiri. Sehingga aman membuka popup dari situs pihak ketiga yang tidak terpercaya. \n2. Sangat mudah untuk membuka popup.\n3. Sebuah popup dapat menavigasi (merubah URL) dan mengirimkan pesan ke pembuka jendela.\n\n## Pemblokiran Popup\n\nDi masa lalu, situs jahat sering sekali menyalahgunakan popups. Sebuah halaman jahat dapat membuka banyak sekali jendela popup dengan iklan. Sehingga saat ini kebanyakan peramban mencoba untuk memblokir dan melindungi pengguna.\n\n**Kebanyakan peramban akan memblokir popups jika dipangil dari luar aktifitas yang dipicu oleh pengguna seperti `onclick`.**\n\nSebgai contoh:\n```js\n// popup diblokir\nwindow.open('https://javascript.info');\n\n// popup diperbolehkan\nbutton.onclick = () => {\n  window.open('https://javascript.info');\n};\n```\n\nDengan cara ini pengguna agak terlindungi dari popups yang tidak diinginkan dan fungsionalitasnya tidak dinonaktifkan secara total.\nBagaimana jika popups dibuka dari `onclick`, tetapi setelah `setTimeout` ? Hal ini sedikit rumit.\n\nCoba kode berikut:\n\n```js run\n// terbuka setelah 3 detik\nsetTimeout(() => window.open('http://google.com'), 3000);\n```\n\nPopup terbuka di Chrome, namun diblokir di Firefox.\n\n...Jika kita mengurangi penundaan, popup akan bekerja di Firefox juga:\n\n```js run\n// open after 1 seconds\nsetTimeout(() => window.open('http://google.com'), 1000);\n```\n\nPerbedaannya adalah Firefox memperlakukan sebuah timeout antara 2000ms atau kurang dari itu untuk dapat diterima, namun lebih dari itu -- hilangkan \"kepercayaan\", Firefox berasumsi bahwa saat ini \"diluar kendali pengguna\". Sehingga yang pertama akan diblokir, dan yang kedua tidak.\n\n## window.open\n\nSintak untuk membuka popup adalah: `window.open(url, name, params)`:\nurl\n: URL yang akan dimuat di dalam jendela baru.\n\nnama\n: nama dari jendela baru. Setiap jendela memiliki sebuah `window.name`, dan dengan ini kita bisa secara spesifik menentukan jendela mana yang digunakan untuk popup. Jika telah ada jendela menggunakan nama tersebut -- URL akan menjadi gantinya, jika tidak jendela baru terbuka.\n\nparameter\n: Konfigurasi untuk jendela baru. Mengandung pengaturan, yang dipisahkan dengan koma. Tidak boleh ada spasi di dalam parameter, sebagai contoh: `width=200,height=100`.\n\nPengaturan untuk `params`:\n\n- Posisi:\n  - `left/top` (numeric) -- mengatur sudut jendela atas-kanan di layar. Namun ada batasan: sebuah jendela baru tidak bisa diposisikan tersembunyi.\n  - `width/height` (numeric) -- Lebar dan tinggi dari jendela baru. Namun ada batasan pada Lebar/tinggi minimal, sehingga tidak mungkin untuk membuat jendela tidak terlihat.\n- Fitur jendela:\n  - `menubar` (yes/no) -- menampilkan atau menyembunyikan menu peramban pada jendela baru.\n  - `toolbar` (yes/no) -- menampilkan atau menyembunyikan navigasi bar peramban (kembali, kedepan, isi ulang dan sebagainya) pada jendela baru.\n  - `location` (yes/no) -- menampilkan atau menyembunyikan URL pada jendela baru. FF dan IE tidak mengizinkan untuk meyembunyikan URL secara <em>default</em>\n  - `status` (yes/no) -- menampilkan atau menyembunyikan <em>bar status</em>. Sekali lagi kebanyakan peramban memaksa untuk menampilkannya.\n  - `resizable` (yes/no) -- mengizinkan atau menolak untuk merubah ukuran jendela baru. Tidak direkomendasikan.\n  - `scrollbars` (yes/no) -- mengizinkan atau menolak <em>scrollbars</em>. untuk jendela baru. Tidak direkomendasikan.\n\nAda juga sedikit dukungan untuk fitur spesifik peramban, Dimana biasanya tidak digunakan. Periksa <a href=\"https://developer.mozilla.org/en/DOM/window.open\">window.open in MDN</a> Sebagai contoh.\n\n## Contoh: sebuah jendela sederhana\nMari buka jendela dengan pengaturan fitur paling sedikit untuk melihat fitur mana yang akan diizinkan atau tidak oleh peramban:\n```js run\nlet params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,\nwidth=0,height=0,left=-1000,top=-1000`;\n\nopen('/', 'test', params);\n```\n\nDalam contoh kebanyakan \"fitur jendela\" telah dinonaktifkan dan jendela berada di luar layar. Jalankan dan lihat apa yang terjadi. Kebanyakan peramban akan \"Memperbaiki\" hal-hal yang ganjil seperti `width/height` yang kosong dan `left/top` yang keluar jendela. Sebagai contoh, Chrome membuka semacam jendela dengan  lebar/tinggi penuh. sehingga akan menempati layar penuh.\n\nMari tambahkan opsi penempatan normal dan masuk akal untuk koordinat `width`, `height`, `left`, `top`:\n```js run\nlet params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,\nwidth=600,height=300,left=100,top=100`;\n\nopen('/', 'test', params);\n```\n\nKebanyakan peramban menampilkan contoh diatas sesuai dengan yang diinginkan.\n\nAturan untuk penggaturan yang dihilangkan:\n\n- Jika tidak ada argumen ketiga di dalam pemanggilan `open`, atau kosong, maka parameter <em>default</em> jendela yang akan digunakan.\n- Jika ada serangkaian parameter, namun sebagian fitur `yes/no` diabaikan, maka fitur yang diabaikan akan diasumsikan untuk memiliki nilai `no`. Sehingga jika anda menetapkan parameter, pastikan secara eksplisit anda telah menyetel semua fitur yang dibutuhkan ke <em>yes</em>.\n- Jika tidak ada `left/top` di dalam parameter, maka peramban akan mencoba untuk membuka sebuah jendela baru didekat jendela yang terakhir terbuka.\n- Jika tidak ada `width/height`, maka jendela baru akan memiliki ukuran yang sama seperti jendela yang terakhir terbuka.\n\n## Mengakses popup dari jendela\nPemanggilan `Open` mengembalikan referensi ke jendela baru. referensi itu bisa digunakan untuk memanipulasi properti, merubah lokasi dan melakukan hal lain yang lebih dari itu.\n\nPada contoh ini, kita menghasilkan popup konten dari Javascript:\n```js\nlet newWin = window.open(\"about:blank\", \"hello\", \"width=200,height=200\");\n\nnewWin.document.write(\"Hello, world!\");\n```\n\nDan disini kita memodifikasi konten setelah dimuat:\n\n```js run\nlet newWindow = open('/', 'example', 'width=300,height=300')\nnewWindow.focus();\n\nalert(newWindow.location.href); // (*) about:blank, loading belum dimulai\n\nnewWindow.onload = function() {\n  let html = `<div style=\"font-size:30px\">Welcome!</div>`;\n*!*\n  newWindow.document.body.insertAdjacentHTML('afterbegin', html);\n*/!*\n};\n```\n\nPenting untuk dicatat: Segera setelah `window.open`, saat itu jendela baru belum dimuat. Hal itu didemonstrasikan oleh `alert` di baris `(*)`, Sehingga kita menunggu untuk `onload` untuk memodifikasinya. Kita juga bisa menggunakan <em>handler</em> `DOMContentLoaded` untuk `newWin.document`.\n\n```warn header=\"Same origin policy\"\nJendela dapat dengan leluasa mengakses konten satu sama lain hanya jika mereka datang dari asal yang sama (protocol://domain:port yang sama)\nJika tidak, semisal jendela utama datang dari `site.com`, dan popup datang dari `gmail.com`, hal ini mustahil dilakukan demi alasan keamanan penguna. Untuk lebih detail lihat bagian <info:cross-window-communication>.\n```\n\n## Mengakses jendela dari popup\nPopup mungkin mengakses \"pembuka\" jendela menggunakan referensei `window.openner`. Ini akan menjadi `null` dari semua jendela kecuali popup. \nJika code dibawah dijalankan, maka konten pembuka jendela saat ini akan dibuah menjadi \"Test\":\n```js run\nlet newWin = window.open(\"about:blank\", \"hello\", \"width=200,height=200\");\n\nnewWin.document.write(\n  \"<script>window.opener.document.body.innerHTML = 'Test'<\\/script>\"\n);\n```\n\nSehingga terjadi koneksi dua arah antara jendela: jendela utama dan popup memiliki sebuah referensi satu sama lain.\n## Menutup sebuah popup\n\nUntuk menutup sebuah jendela: `win.close()`. \n\nUntuk memeriksa apakah jendela sudah tertutup: `win.closed`.\n\nSecara teknis, metode `close()` tersedia pada setiap `window`, namun `window.close()` diabaikan oleh kebanyakan beramban jika `window` tidak dibuat dengan `window.open`. Jadi hanya akan bekerja untuk popup.\n\nproperti `closed` bernilai `true` jika jendela ditutup. Hal ini berguna untuk memeriksa apakah popup (atau jendela utama) masih terbuka atau tidak. seorang pengguna bisa menutupnya kapan saja, dan kode kita mengambil pertimbangan untuk diperhitungkan.\n\nCode dibawah ini dimuat dan kemudian menutup jendela:\n\n```js run\nlet newWindow = open('/', 'example', 'width=300,height=300');\n\nnewWindow.onload = function() {\n  newWindow.close();\n  alert(newWindow.closed); // benar\n};\n```\n\n\n## berpindah dan merubah ukuran.\n\nAda metode untuk memindahkan/merubah ukuran sebuah jendela:\n\n`win.moveBy(x,y)`\n: Memindahkan jendela ke posisi `x` piksel ke kanan dan `y` piksel kebawah. Nilai negatif dapat diterima (untuk memindahkan kiri/atas).\n\n`win.moveTo(x,y)`\n: Memindahkan jendela ke koordinat `(x,y)` di layar.\n\n`win.resizeBy(width,height)`\n: Merubah ukuran jendela dengan memberikan `width/height` ke ukuran sat ini. Nilai negatif dapat diterima.\n\n`win.resizeTo(width,height)`\n: Merubah ukuran jendela ke ukuran yang diberikan.\n\nAda juga `window.onresize` <em>event</em>.\n\n```warn header=\"Hanya popups\"\nUntuk mencegah penyalahgunaan, peramban biasanya memblokir metode ini. Mereka hanya bekerja baik pada popup yang kami buka, yang tidak memiliki tab tambahan.\n```\n\n```warn header=\"Tidak ada modifikasi/maksimasi\"\nJavascript tidak memiliki cara untuk mengecilkan atau memaksimalkan sebuah jendela. fungsi OS-level ini tersembunyi dari pengembang frontend\nmetode Move/resize tidak dapat berjalan pada jendela maximized/minimized.\n```\n\n## Gulir jendela\n\nKami telah membicarakan tentang mengulir jendela di bagian <info:size-and-scroll-window>.\n`win.scrollBy(x,y)`\n: Gulir jendela `x` piksel ke kanan dan `y` kebawah terhadap posisi gulir saat ini. Nilai negatif dapat diterima.\n`win.scrollTo(x,y)`\n: Scroll the window to the given coordinates `(x,y)`.\n\n`elem.scrollIntoView(top = true)`\n: Mengulir jendela untuk membuat `elem` terlihat diatas atau di bawah dari `elem.scrollIntoView(false)`, Kemudian ada juga <em>event</em> `window.onscroll`.\n\n## Fokus/kabur di jendela\nSecara teori, ada metode `window.focus()` dan `window.blur()` untuk memfokuskan/tidak fokus sebuah jendela. Dan ada juga <em>even</em> `focus/blur` yang mengizinkan untuk menangkap momen saat pegunjung fokus pada jendela dan berpindah ke tempat lain.\n\nMeskipun, dalam praktiknya hal ini dibatasi, karena pada masa lalu halaman jahat menyalahgunakannya.\n\nSebagai contoh, lihat code ini:\n```js run\nwindow.onblur = () => window.focus();\n```\n\nMesikipun pengguna mencoba untuk berpindah dari jendela (`window.onblur`), hal ini membawa jendela kembali fokus. Tujuanya adalah untuk \"mengunci\" pengguna selama berada di dalam `window`.\n\nSehingga peramban harus memperkenalkan banyak batasan kode seperti ini dan melindungi pengguna dari iklan dan halaman jahat. Mereka bergantung ke peramban.\n\nSebagai contoh, sebuah peramban <em>mobile</em> pada umumnya mengabaikan `window.focus()`. Dan fokus tidak bekerja saat sebuah popup terbuka di dalam tab yang terpisah daripada membuka sebuah jendela baru.\n\nMasih ada beberapa kasus pengunaan menggunakan pangilan sejenis untuk menjalankan dan berguna.\n\nSebagai contoh:\n- Saat kita membuka popup, mungkin ide yang bagus untuk menjalankan `newWindow.focus()`. untuk beberapa kombinasi OS/peramban memastikan bahwa pengguna saat ini berada di dalam jendela baru\n- Jika kita ingin melacak kapan pengunjung mengunakan web-app, kita dapat melacak `window.onfocus/onblur`. Hal ini mengijinkan kita untuk menangguhkan / melanjutkan di dalam aktifitas animasi dan semacamnya. Tetapi tolong dicatat bahwa <em>event</em> `blur` berarti pengunjung berpindah dari jendela, teteapi mereka mungkin masih mengamatinya. Jendela berada di latar belakang, namun mungkin masih dapat dilihat.\n\n## kesimpulan\nJendela popup jarang digunakan, karena ada beberapa alternatif: memuat dan menampilkan informasi in-page, atau di dalam iframe.\n\nJika kita akan membuka popup, cara yang benar adalah dengan memberitahu pengguna tentang hal ini. Sebuah ikon \"jendela terbuka\" dekat tautan atau tombol yang mengizinkan pengunjung untuk tetap fokus dan ingat kedua jendela. \n\n- Popup dapat dibuka dengan pemanggilan `open(url, name, params)`. hal ini akan mengembalikan referensi untuk jendela yang lebih baru.\n- Peramban memblokir pemanggilan `open` dari kode yang berasal dari luar aksi pengguna. Biasanya notifikasi muncul, sehingga pengguna mungkin mengizinkannya.\n- Secara umum peramban membuka tab baru, tetapi jika ukuran disediakan, maka akan terbuka jendela popup. \n- Popup mungkin mengakses pembuka jendela menggunakan properti `window.opener`.\n- Jendela utama dan popup dapat secara bebas membaca dan memodifikasi satu sama lain jika mereka memiliki asal yang sama. Jika tidak mereka dapat merubah lokasi satu sama lain dan [bertukar pesan](info:cross-window-communication).\n\nUntuk menutuo popup: gunakan pemanggilan `close()`. Juga pengguna mungkin menutupnya (seperti jendela yang lain). `window.closed` adalah `true` setelahnya.\n\n- Metode `focus()` dan `blur()` mengizinkan sebuah jendela untuk fokus/ tidak fokus. Tetapi mereka tidak bekerja setiap saat. \n- `focus` dan `blur` <em>even</em> mengizinkan untuk melacak perpindahan masuk dan keluar jendela. Namun tolong dicatat bahwa sebuah jendela mungkin masih dapat terlihat meskipun di dalam status latar belakang, setelah `blur`.\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/article.md",
    "content": "# Cross-window communication\n\nThe \"Same Origin\" (same site) policy limits access of windows and frames to each other.\n\nThe idea is that if a user has two pages open: one from `john-smith.com`, and another one is `gmail.com`, then they wouldn't want a script from `john-smith.com` to read our mail from `gmail.com`. So, the purpose of the \"Same Origin\" policy is to protect users from information theft.\n\n## Same Origin [#same-origin]\n\nTwo URLs are said to have the \"same origin\" if they have the same protocol, domain and port.\n\nThese URLs all share the same origin:\n\n- `http://site.com`\n- `http://site.com/`\n- `http://site.com/my/page.html`\n\nThese ones do not:\n\n- <code>http://<b>www.</b>site.com</code> (another domain: `www.` matters)\n- <code>http://<b>site.org</b></code> (another domain: `.org` matters)\n- <code><b>https://</b>site.com</code> (another protocol: `https`)\n- <code>http://site.com:<b>8080</b></code> (another port: `8080`)\n\nThe \"Same Origin\" policy states that:\n\n- if we have a reference to another window, e.g. a popup created by `window.open` or a window inside `<iframe>`, and that window comes from the same origin, then we have full access to that window.\n- otherwise, if it comes from another origin, then we can't access the content of that window: variables, document, anything. The only exception is `location`: we can change it (thus redirecting the user). But we cannot *read* location (so we can't see where the user is now, no information leak).\n\n### In action: iframe\n\nAn `<iframe>` tag hosts a separate embedded window, with its own separate `document` and `window` objects.\n\nWe can access them using properties:\n\n- `iframe.contentWindow` to get the window inside the `<iframe>`.\n- `iframe.contentDocument` to get the document inside the `<iframe>`, shorthand for `iframe.contentWindow.document`.\n\nWhen we access something inside the embedded window, the browser checks if the iframe has the same origin. If that's not so then the access is denied (writing to `location` is an exception, it's still permitted).\n\nFor instance, let's try reading and writing to `<iframe>` from another origin:\n\n```html run\n<iframe src=\"https://example.com\" id=\"iframe\"></iframe>\n\n<script>\n  iframe.onload = function() {\n    // we can get the reference to the inner window\n*!*\n    let iframeWindow = iframe.contentWindow; // OK\n*/!*\n    try {\n      // ...but not to the document inside it\n*!*\n      let doc = iframe.contentDocument; // ERROR\n*/!*\n    } catch(e) {\n      alert(e); // Security Error (another origin)\n    }\n\n    // also we can't READ the URL of the page in iframe\n    try {\n      // Can't read URL from the Location object\n*!*\n      let href = iframe.contentWindow.location.href; // ERROR\n*/!*\n    } catch(e) {\n      alert(e); // Security Error\n    }\n\n    // ...we can WRITE into location (and thus load something else into the iframe)!\n*!*\n    iframe.contentWindow.location = '/'; // OK\n*/!*\n\n    iframe.onload = null; // clear the handler, not to run it after the location change\n  };\n</script>\n```\n\nThe code above shows errors for any operations except:\n\n- Getting the reference to the inner window `iframe.contentWindow` - that's allowed.\n- Writing to `location`.\n\nContrary to that, if the `<iframe>` has the same origin, we can do anything with it:\n\n```html run\n<!-- iframe from the same site -->\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  iframe.onload = function() {\n    // just do anything\n    iframe.contentDocument.body.prepend(\"Hello, world!\");\n  };\n</script>\n```\n\n```smart header=\"`iframe.onload` vs `iframe.contentWindow.onload`\"\nThe `iframe.onload` event (on the `<iframe>` tag) is essentially the same as `iframe.contentWindow.onload` (on the embedded window object). It triggers when the embedded window fully loads with all resources.\n\n...But we can't access `iframe.contentWindow.onload` for an iframe from another origin, so using `iframe.onload`.\n```\n\n## Windows on subdomains: document.domain\n\nBy definition, two URLs with different domains have different origins.\n\nBut if windows share the same second-level domain, for instance `john.site.com`, `peter.site.com` and `site.com` (so that their common second-level domain is `site.com`), we can make the browser ignore that difference, so that they can be treated as coming from the \"same origin\" for the purposes of cross-window communication.\n\nTo make it work, each such window should run the code:\n\n```js\ndocument.domain = 'site.com';\n```\n\nThat's all. Now they can interact without limitations. Again, that's only possible for pages with the same second-level domain.\n\n## Iframe: wrong document pitfall\n\nWhen an iframe comes from the same origin, and we may access its  `document`, there's a pitfall. It's not related to cross-origin things, but important to know.\n\nUpon its creation an iframe immediately has a document. But that document is different from the one that loads into it!\n\nSo if we do something with the document immediately, that will probably be lost.\n\nHere, look:\n\n\n```html run\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  let oldDoc = iframe.contentDocument;\n  iframe.onload = function() {\n    let newDoc = iframe.contentDocument;\n*!*\n    // the loaded document is not the same as initial!\n    alert(oldDoc == newDoc); // false\n*/!*\n  };\n</script>\n```\n\nWe shouldn't work with the document of a not-yet-loaded iframe, because that's the *wrong document*. If we set any event handlers on it, they will be ignored.\n\nHow to detect the moment when the document is there?\n\nThe right document is definitely at place when `iframe.onload`  triggers. But it only triggers when the whole iframe with all resources is loaded.\n\nWe can try to catch the moment earlier using checks in `setInterval`:\n\n```html run\n<iframe src=\"/\" id=\"iframe\"></iframe>\n\n<script>\n  let oldDoc = iframe.contentDocument;\n\n  // every 100 ms check if the document is the new one\n  let timer = setInterval(() => {\n    let newDoc = iframe.contentDocument;\n    if (newDoc == oldDoc) return;\n\n    alert(\"New document is here!\");\n\n    clearInterval(timer); // cancel setInterval, don't need it any more\n  }, 100);\n</script>\n```\n\n## Collection: window.frames\n\nAn alternative way to get a window object for `<iframe>` -- is to get it from the named collection  `window.frames`:\n\n- By number: `window.frames[0]` -- the window object for the first frame in the document.\n- By name: `window.frames.iframeName` -- the window object for the frame with  `name=\"iframeName\"`.\n\nFor instance:\n\n```html run\n<iframe src=\"/\" style=\"height:80px\" name=\"win\" id=\"iframe\"></iframe>\n\n<script>\n  alert(iframe.contentWindow == frames[0]); // true\n  alert(iframe.contentWindow == frames.win); // true\n</script>\n```\n\nAn iframe may have other iframes inside. The corresponding `window` objects form a hierarchy.\n\nNavigation links are:\n\n- `window.frames` -- the collection of \"children\" windows (for nested frames).\n- `window.parent` -- the reference to the \"parent\" (outer) window.\n- `window.top` -- the reference to the topmost parent window.\n\nFor instance:\n\n```js run\nwindow.frames[0].parent === window; // true\n```\n\nWe can use the `top` property to check if the current document is open inside a frame or not:\n\n```js run\nif (window == top) { // current window == window.top?\n  alert('The script is in the topmost window, not in a frame');\n} else {\n  alert('The script runs in a frame!');\n}\n```\n\n## The \"sandbox\" iframe attribute\n\nThe `sandbox` attribute allows for the exclusion of certain actions inside an `<iframe>` in order to prevent it executing untrusted code. It \"sandboxes\" the iframe by treating it as coming from another origin and/or applying other limitations.\n\nThere's a \"default set\" of restrictions applied for `<iframe sandbox src=\"...\">`. But it can be relaxed if we provide a space-separated list of restrictions that should not be applied as a value of the attribute, like this: `<iframe sandbox=\"allow-forms allow-popups\">`.\n\nIn 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.\n\nHere's a list of limitations:\n\n`allow-same-origin`\n: 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.\n\n`allow-top-navigation`\n: Allows the `iframe` to change `parent.location`.\n\n`allow-forms`\n: Allows to submit forms from `iframe`.\n\n`allow-scripts`\n: Allows to run scripts from the `iframe`.\n\n`allow-popups`\n: Allows to `window.open` popups from the `iframe`\n\nSee [the manual](mdn:/HTML/Element/iframe) for more.\n\nThe example below demonstrates a sandboxed iframe with the default set of restrictions: `<iframe sandbox src=\"...\">`. It has some JavaScript and a form.\n\nPlease note that nothing works. So the default set is really harsh:\n\n[codetabs src=\"sandbox\" height=140]\n\n\n```smart\nThe 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.\n```\n\n## Cross-window messaging\n\nThe `postMessage` interface allows windows to talk to each other no matter which origin they are from.\n\nSo, it's a way around the \"Same Origin\" policy. It allows a window from `john-smith.com` to talk to `gmail.com` and exchange information, but only if they both agree and call corresponding JavaScript functions. That makes it safe for users.\n\nThe interface has two parts.\n\n### postMessage\n\nThe 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)`.\n\nArguments:\n\n`data`\n: The data to send. Can be any object, the data is cloned using the \"structured serialization algorithm\". IE supports only strings, so we should `JSON.stringify` complex objects to support that browser.\n\n`targetOrigin`\n: Specifies the origin for the target window, so that only a window from the given origin will get the message.\n\nThe `targetOrigin` is a safety measure. Remember, if the target window comes from another origin, we can't read it's `location` in the sender window. So we can't be sure which site is open in the intended window right now: the user could navigate away, and the sender window has no idea about it.\n\nSpecifying `targetOrigin` ensures that the window only receives the data if it's still at the right site. Important when the data is sensitive.\n\nFor instance, here `win` will only receive the message if it has a document from the origin `http://example.com`:\n\n```html no-beautify\n<iframe src=\"http://example.com\" name=\"example\">\n\n<script>\n  let win = window.frames.example;\n\n  win.postMessage(\"message\", \"http://example.com\");\n</script>\n```\n\nIf we don't want that check, we can set `targetOrigin` to `*`.\n\n```html no-beautify\n<iframe src=\"http://example.com\" name=\"example\">\n\n<script>\n  let win = window.frames.example;\n\n*!*\n  win.postMessage(\"message\", \"*\");\n*/!*\n</script>\n```\n\n\n### onmessage\n\nTo 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).\n\nThe event object has special properties:\n\n`data`\n: The data from `postMessage`.\n\n`origin`\n: The origin of the sender, for instance `http://javascript.info`.\n\n`source`\n: The reference to the sender window. We can immediately `source.postMessage(...)` back if we want.\n\nTo assign that handler, we should use `addEventListener`, a short syntax `window.onmessage` does not work.\n\nHere's an example:\n\n```js\nwindow.addEventListener(\"message\", function(event) {\n  if (event.origin != 'http://javascript.info') {\n    // something from an unknown domain, let's ignore it\n    return;\n  }\n\n  alert( \"received: \" + event.data );\n\n  // can message back using event.source.postMessage(...)\n});\n```\n\nThe full example:\n\n[codetabs src=\"postmessage\" height=120]\n\n## Summary\n\nTo call methods and access the content of another window, we should first have a reference to it.\n\nFor popups we have these references:\n- From the opener window: `window.open` -- opens a new window and returns a reference to it,\n- From the popup: `window.opener` -- is a reference to the opener window from a popup.\n\nFor iframes, we can access parent/children windows using:\n- `window.frames` -- a collection of nested window objects,\n- `window.parent`, `window.top` are the references to parent and top windows,\n- `iframe.contentWindow` is the window inside an `<iframe>` tag.\n\nIf windows share the same origin (host, port, protocol), then windows can do whatever they want with each other.\n\nOtherwise, only possible actions are:\n- Change the `location` of another window (write-only access).\n- Post a message to it.\n\nExceptions are:\n- 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.\n- 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.\n\nThe `postMessage` interface allows two windows with any origins to talk:\n\n1. The sender calls `targetWin.postMessage(data, targetOrigin)`.\n2. If `targetOrigin` is not `'*'`, then the browser checks if window `targetWin` has the origin `targetOrigin`.\n3. If it is so, then `targetWin` triggers the `message` event with special properties:\n    - `origin` -- the origin of the sender window (like `http://my.site.com`)\n    - `source` -- the reference to the sender window.\n    - `data` -- the data, any object in everywhere except IE that supports only strings.\n\n    We should use `addEventListener` to set the handler for this event inside the target window.\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/postmessage.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  Receiving iframe.\n  <script>\n    window.addEventListener('message', function(event) {\n      alert(`Received ${event.data} from ${event.origin}`);\n    });\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/postmessage.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <form id=\"form\">\n    <input type=\"text\" placeholder=\"Enter message\" name=\"message\">\n    <input type=\"submit\" value=\"Click to send\">\n  </form>\n\n  <iframe src=\"iframe.html\" id=\"iframe\" style=\"display:block;height:60px\"></iframe>\n\n  <script>\n    form.onsubmit = function() {\n      iframe.contentWindow.postMessage(this.message.value, '*');\n      return false;\n    };\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/sandbox.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <div>The iframe below has the <code>sandbox</code> attribute.</div>\n\n  <iframe sandbox src=\"sandboxed.html\" style=\"height:60px;width:90%\"></iframe>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/03-cross-window-communication/sandbox.view/sandboxed.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <button onclick=\"alert(123)\">Click to run a script (doesn't work)</button>\n\n  <form action=\"http://google.com\">\n    <input type=\"text\">\n    <input type=\"submit\" value=\"Submit (doesn't work)\">\n  </form>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/article.md",
    "content": "# The clickjacking attack\n\nThe \"clickjacking\" attack allows an evil page to click on a \"victim site\" *on behalf of the visitor*.\n\nMany sites were hacked this way, including Twitter, Facebook, Paypal and other sites. They have all been fixed, of course.\n\n## The idea\n\nThe idea is very simple.\n\nHere's how clickjacking was done with Facebook:\n\n1. A visitor is lured to the evil page. It doesn't matter how.\n2. The page has a harmless-looking link on it (like \"get rich now\" or \"click here, very funny\").\n3. 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`.\n4. In attempting to click the link, the visitor in fact clicks the button.\n\n## The demo\n\nHere's how the evil page looks. To make things clear, the `<iframe>` is half-transparent (in real evil pages it's fully transparent):\n\n```html run height=120 no-beautify\n<style>\niframe { /* iframe from the victim site */\n  width: 400px;\n  height: 100px;\n  position: absolute;\n  top:0; left:-20px;\n*!*\n  opacity: 0.5; /* in real opacity:0 */\n*/!*\n  z-index: 1;\n}\n</style>\n\n<div>Click to get rich now:</div>\n\n<!-- The url from the victim site -->\n*!*\n<iframe src=\"/clickjacking/facebook.html\"></iframe>\n\n<button>Click here!</button>\n*/!*\n\n<div>...And you're cool (I'm a cool hacker actually)!</div>\n```\n\nThe full demo of the attack:\n\n[codetabs src=\"clickjacking-visible\" height=160]\n\nHere 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.\n\nAs 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.\n\nHere's the same example, but closer to reality, with `opacity:0` for `<iframe>`:\n\n[codetabs src=\"clickjacking\" height=160]\n\nAll 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. So that when a user clicks the link, they actually click the button. That's usually doable with CSS.\n\n```smart header=\"Clickjacking is for clicks, not for keyboard\"\nThe attack only affects mouse actions (or similar, like taps on mobile).\n\nKeyboard input is much difficult to redirect. 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 they see on the page, they actually focus on the input inside the iframe.\n\nBut then there's a problem. Everything that the visitor types will be hidden, because the iframe is not visible.\n\nPeople will usually stop typing when they can't see their new characters printing on the screen.\n```\n\n## Old-school defences (weak)\n\nThe oldest defence is a bit of JavaScript which forbids opening the page in a frame (so-called \"framebusting\").\n\nThat looks like this:\n\n```js\nif (top != window) {\n  top.location = window.location;\n}\n```\n\nThat is: if the window finds out that it's not on top, then it automatically makes itself the top.\n\nThis not a reliable defence, because there are many ways to hack around it. Let's cover a few.\n\n### Blocking top-navigation\n\nWe can block the transition caused by changing `top.location` in  [beforeunload](info:onload-ondomcontentloaded#window.onbeforeunload) event handler.\n\nThe top page (enclosing one, belonging to the hacker) sets a preventing handler to it, like this:\n\n```js\nwindow.onbeforeunload = function() {\n  return false;\n};\n```\n\nWhen the `iframe` tries to change `top.location`, the visitor gets a message asking them whether they want to leave.\n\nIn most cases the visitor would answer negatively because they don't know about the iframe - all they can see is the top page, there's no reason to leave. So `top.location` won't change!\n\nIn action:\n\n[codetabs src=\"top-location\"]\n\n### Sandbox attribute\n\nOne of the things restricted by the `sandbox` attribute is navigation. A sandboxed iframe may not change `top.location`.\n\nSo 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.\n\nHere's the code:\n\n```html\n<iframe *!*sandbox=\"allow-scripts allow-forms\"*/!* src=\"facebook.html\"></iframe>\n```\n\nThere are other ways to work around that simple protection too.\n\n## X-Frame-Options\n\nThe server-side header `X-Frame-Options` can permit or forbid displaying the page inside a frame.\n\nIt must be sent exactly as HTTP-header: the browser will ignore it if found in HTML `<meta>` tag. So, `<meta http-equiv=\"X-Frame-Options\"...>` won't do anything.\n\nThe header may have 3 values:\n\n\n`DENY`\n: Never ever show the page inside a frame.\n\n`SAMEORIGIN`\n: Allow inside a frame if the parent document comes from the same origin.\n\n`ALLOW-FROM domain`\n: Allow inside a frame if the parent document is from the given domain.\n\nFor instance, Twitter uses `X-Frame-Options: SAMEORIGIN`.\n\n````online\nHere's the result:\n\n```html\n<iframe src=\"https://twitter.com\"></iframe>\n```\n\n<!-- ebook: prerender/ chrome headless dies and timeouts on this iframe -->\n<iframe src=\"https://twitter.com\"></iframe>\n\nDepending 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.\n````\n\n## Showing with disabled functionality\n\nThe `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.\n\nSo there are other solutions... For instance, we can \"cover\" the page with a `<div>` with styles `height: 100%; width: 100%;`, so that it will intercept all clicks. That `<div>` is to be removed if `window == top` or if we figure out that we don't need the protection.\n\nSomething like this:\n\n```html\n<style>\n  #protector {\n    height: 100%;\n    width: 100%;\n    position: absolute;\n    left: 0;\n    top: 0;\n    z-index: 99999999;\n  }\n</style>\n\n<div id=\"protector\">\n  <a href=\"/\" target=\"_blank\">Go to the site</a>\n</div>\n\n<script>\n  // there will be an error if top window is from the different origin\n  // but that's ok here\n  if (top.document.domain == document.domain) {\n    protector.remove();\n  }\n</script>\n```\n\nThe demo:\n\n[codetabs src=\"protector\"]\n\n## Samesite cookie attribute\n\nThe `samesite` cookie attribute can also prevent clickjacking attacks.\n\nA cookie with such attribute is only sent to a website if it's opened directly, not via a frame, or otherwise. More information in the chapter <info:cookie#samesite>.\n\nIf the site, such as Facebook, had `samesite` attribute on its authentication cookie, like this:\n\n```\nSet-Cookie: authorization=secret; samesite\n```\n\n...Then such cookie wouldn't be sent when Facebook is open in iframe from another site. So the attack would fail.\n\nThe `samesite` cookie attribute will not have an effect when cookies are not used. This may allow other websites to easily show our public, unauthenticated pages in iframes.\n\nHowever, this may also allow clickjacking attacks to work in a few limited cases. An anonymous polling website that prevents duplicate voting by checking IP addresses, for example, would still be vulnerable to clickjacking because it does not authenticate users using cookies.\n\n## Summary\n\nClickjacking is a way to \"trick\" users into clicking on a victim site without even knowing what's happening. That's dangerous if there are important click-activated actions.\n\nA hacker can post a link to their evil page in a message, or lure visitors to their page by some other means. There are many variations.\n\nFrom 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 they may use cunning messages to coerce the user into clicking on them as well.\n\nThe 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.\n\n- It is recommended to use `X-Frame-Options: SAMEORIGIN` on pages (or whole websites) which are not intended to be viewed inside frames.\n- Use a covering `<div>` if we want to allow our pages to be shown in iframes, but still stay safe.\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking-visible.view/facebook.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body style=\"margin:10px;padding:10px\">\n\n  <input type=\"button\" onclick=\"alert('Like pressed on facebook.html!')\" value=\"I LIKE IT !\">\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking-visible.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 5px;\n      left: -14px;\n      opacity: 0.5;\n      z-index: 1;\n    }\n  </style>\n\n  <div>Click to get rich now:</div>\n\n  <!-- The url from the victim site -->\n  <iframe src=\"facebook.html\"></iframe>\n\n  <button>Click here!</button>\n\n  <div>...And you're cool (I'm a cool hacker actually)!</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking.view/facebook.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<body style=\"margin:10px;padding:10px\">\n\n  <input type=\"button\" onclick=\"alert('Like pressed on facebook.html!')\" value=\"I LIKE IT !\">\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/clickjacking.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 5px;\n      left: -14px;\n      opacity: 0;\n      z-index: 1;\n    }\n  </style>\n\n  <div>Click to get rich now:</div>\n\n  <!-- The url from the victim site -->\n  <iframe src=\"facebook.html\"></iframe>\n\n  <button>Click here!</button>\n\n  <div>...And you're cool (I'm a cool hacker actually)!</div>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/protector.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n\n  <style>\n    #protector {\n      height: 100%;\n      width: 100%;\n      position: absolute;\n      left: 0;\n      top: 0;\n      z-index: 99999999;\n    }\n  </style>\n\n</head>\n\n<body>\n\n<div id=\"protector\">\n  <a href=\"/\" target=\"_blank\">Go to the site</a>\n</div>\n\n<script>\n\n  if (top.document.domain == document.domain) {\n    protector.remove();\n  }\n\n</script>\n\n  This text is always visible.\n\n  But if the page was open inside a document from another domain, the div over it would prevent any actions.\n\n  <button onclick=\"alert(1)\">Click wouldn't work in that case</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/protector.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n<body>\n\n  <iframe src=\"iframe.html\"></iframe>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/top-location.view/iframe.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n</head>\n\n<body>\n\n  <div>Changes top.location to javascript.info</div>\n\n  <script>\n    top.location = 'https://javascript.info';\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/06-clickjacking/top-location.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n\n  <style>\n    iframe {\n      width: 400px;\n      height: 100px;\n      position: absolute;\n      top: 0;\n      left: -20px;\n      opacity: 0;\n      z-index: 1;\n    }\n  </style>\n\n  <script>\n    function attack() {\n\n      window.onbeforeunload = function() {\n        window.onbeforeunload = null;\n        return \"Want to leave without learning all the secrets (he-he)?\";\n      };\n\n      document.body.insertAdjacentHTML('beforeend', '<iframe src=\"iframe.html\">');\n    }\n  </script>\n</head>\n\n<body>\n\n  <p>After a click on the button the visitor gets a \"strange\" question about whether they want to leave.</p>\n\n  <p>Probably they would respond \"No\", and the iframe protection is hacked.</p>\n\n  <button onclick=\"attack()\">Add a \"protected\" iframe</button>\n\n</body>\n</html>\n"
  },
  {
    "path": "3-frames-and-windows/index.md",
    "content": "# Frames and windows\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/solution.js",
    "content": "function concat(arrays) {\n  // sum of individual array lengths\n  let totalLength = arrays.reduce((acc, value) => acc + value.length, 0);\n\n  if (!arrays.length) return null;\n\n  let result = new Uint8Array(totalLength);\n\n  // for each array - copy it over result\n  // next array is copied right after the previous one\n  let length = 0;\n  for(let array of arrays) {\n    result.set(array, length);\n    length += array.length;\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/source.js",
    "content": "function concat(arrays) {\n  // ...your code...\n}\n\nlet chunks = [\n  new Uint8Array([0, 1, 2]),\n  new Uint8Array([3, 4, 5]),\n  new Uint8Array([6, 7, 8])\n];\n\nconsole.log(Array.from(concat(chunks))); // 0, 1, 2, 3, 4, 5, 6, 7, 8\n\nconsole.log(concat(chunks).constructor.name); // Uint8Array\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/_js.view/test.js",
    "content": "describe(\"concat\", function() {\n  let chunks = [\n    new Uint8Array([0, 1, 2]),\n    new Uint8Array([3, 4, 5]),\n    new Uint8Array([6, 7, 8])\n  ];\n\n  it(\"result has the same array type\", function() {\n\n    let result = concat(chunks);\n\n    assert.equal(result.constructor, Uint8Array);\n  });\n\n  it(\"concatenates arrays\", function() {\n\n    let result = concat(chunks);\n\n    assert.deepEqual(result, new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8]));\n\n  });\n\n  it(\"returns empty array on empty input\", function() {\n\n    let result = concat([]);\n\n    assert.equal(result.length, 0);\n\n  });\n\n});\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/solution.md",
    "content": ""
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/01-concat/task.md",
    "content": "\n# Concatenate typed arrays\n\nGiven an array of `Uint8Array`, write a function `concat(arrays)` that returns a concatenation of them into a single array.\n"
  },
  {
    "path": "4-binary/01-arraybuffer-binary-arrays/article.md",
    "content": "# ArrayBuffer, binary arrays\n\nIn web-development we meet binary data mostly while dealing with files (create, upload, download). Another typical use case is image processing.\n\nThat's all possible in JavaScript, and binary operations are high-performant.\n\nAlthough, there's a bit of confusion, because there are many classes. To name a few:\n- `ArrayBuffer`, `Uint8Array`, `DataView`, `Blob`, `File`, etc.\n\nBinary data in JavaScript is implemented in a non-standard way, compared to other languages. But when we sort things out, everything becomes fairly simple.\n\n**The basic binary object is `ArrayBuffer` -- a reference to a fixed-length contiguous memory area.**\n\nWe create it like this:\n```js run\nlet buffer = new ArrayBuffer(16); // create a buffer of length 16\nalert(buffer.byteLength); // 16\n```\n\nThis allocates a contiguous memory area of 16 bytes and pre-fills it with zeroes.\n\n```warn header=\"`ArrayBuffer` is not an array of something\"\nLet's eliminate a possible source of confusion. `ArrayBuffer` has nothing in common with `Array`:\n- It has a fixed length, we can't increase or decrease it.\n- It takes exactly that much space in the memory.\n- To access individual bytes, another \"view\" object is needed, not `buffer[index]`.\n```\n\n`ArrayBuffer` is a memory area. What's stored in it? It has no clue. Just a raw sequence of bytes.\n\n**To manipulate an `ArrayBuffer`, we need to use a \"view\" object.**\n\nA view object does not store anything on it's own. It's the \"eyeglasses\" that give an interpretation of the bytes stored in the `ArrayBuffer`.\n\nFor instance:\n\n- **`Uint8Array`** -- treats each byte in `ArrayBuffer` as a separate number, with possible values from 0 to 255 (a byte is 8-bit, so it can hold only that much). Such value is called a \"8-bit unsigned integer\".\n- **`Uint16Array`** -- treats every 2 bytes as an integer, with possible values from 0 to 65535. That's called a \"16-bit unsigned integer\".\n- **`Uint32Array`** -- treats every 4 bytes as an integer, with possible values from 0 to 4294967295. That's called a \"32-bit unsigned integer\".\n- **`Float64Array`** -- treats every 8 bytes as a floating point number with possible values from <code>5.0x10<sup>-324</sup></code> to <code>1.8x10<sup>308</sup></code>.\n\nSo, the binary data in an `ArrayBuffer` of 16 bytes can be interpreted as 16 \"tiny numbers\", or 8 bigger numbers (2 bytes each), or 4 even bigger (4 bytes each), or 2 floating-point values with high precision (8 bytes each).\n\n![](arraybuffer-views.svg)\n\n`ArrayBuffer` is the core object, the root of everything, the raw binary data.\n\nBut if we're going to write into it, or iterate over it, basically for almost any operation – we must use a view, e.g:\n\n```js run\nlet buffer = new ArrayBuffer(16); // create a buffer of length 16\n\n*!*\nlet view = new Uint32Array(buffer); // treat buffer as a sequence of 32-bit integers\n\nalert(Uint32Array.BYTES_PER_ELEMENT); // 4 bytes per integer\n*/!*\n\nalert(view.length); // 4, it stores that many integers\nalert(view.byteLength); // 16, the size in bytes\n\n// let's write a value\nview[0] = 123456;\n\n// iterate over values\nfor(let num of view) {\n  alert(num); // 123456, then 0, 0, 0 (4 values total)\n}\n\n```\n\n## TypedArray\n\nThe common term for all these views (`Uint8Array`, `Uint32Array`, etc) is [TypedArray](https://tc39.github.io/ecma262/#sec-typedarray-objects). They share the same set of methods and properities.\n\nPlease note, there's no constructor called `TypedArray`, it's just a common \"umbrella\" term to represent one of views over `ArrayBuffer`: `Int8Array`, `Uint8Array` and so on, the full list will soon follow.\n\nWhen you see something like `new TypedArray`, it means any of `new Int8Array`, `new Uint8Array`, etc.\n\nTyped arrays behave like regular arrays: have indexes and are iterable.\n\nA typed array constructor (be it `Int8Array` or `Float64Array`, doesn't matter) behaves differently depending on argument types.\n\nThere are 5 variants of arguments:\n\n```js\nnew TypedArray(buffer, [byteOffset], [length]);\nnew TypedArray(object);\nnew TypedArray(typedArray);\nnew TypedArray(length);\nnew TypedArray();\n```\n\n1. If an `ArrayBuffer` argument is supplied, the view is created over it. We used that syntax already.\n\n    Optionally we can provide `byteOffset` to start from (0 by default) and the `length` (till the end of the buffer by default), then the view will cover only a part of the `buffer`.\n\n2. If an `Array`, or any array-like object is given, it creates a typed array of the same length and copies the content.\n\n    We can use it to pre-fill the array with the data:\n    ```js run\n    *!*\n    let arr = new Uint8Array([0, 1, 2, 3]);\n    */!*\n    alert( arr.length ); // 4, created binary array of the same length\n    alert( arr[1] ); // 1, filled with 4 bytes (unsigned 8-bit integers) with given values\n    ```\n3. If another `TypedArray` is supplied, it does the same: creates a typed array of the same length and copies values. Values are converted to the new type in the process, if needed.\n    ```js run\n    let arr16 = new Uint16Array([1, 1000]);\n    *!*\n    let arr8 = new Uint8Array(arr16);\n    */!*\n    alert( arr8[0] ); // 1\n    alert( arr8[1] ); // 232, tried to copy 1000, but can't fit 1000 into 8 bits (explanations below)\n    ```\n\n4. For a numeric argument `length` -- creates the typed array to contain that many elements. Its byte length will be `length` multiplied by the number of bytes in a single item `TypedArray.BYTES_PER_ELEMENT`:\n    ```js run\n    let arr = new Uint16Array(4); // create typed array for 4 integers\n    alert( Uint16Array.BYTES_PER_ELEMENT ); // 2 bytes per integer\n    alert( arr.byteLength ); // 8 (size in bytes)\n    ```\n\n5. Without arguments, creates an zero-length typed array.\n\nWe can create a `TypedArray` directly, without mentioning `ArrayBuffer`. But a view cannot exist without an underlying `ArrayBuffer`, so gets created automatically in all these cases except the first one (when provided).\n\nTo access the `ArrayBuffer`, there are properties:\n- `arr.buffer` -- references the `ArrayBuffer`.\n- `arr.byteLength` -- the length of the `ArrayBuffer`.\n\nSo, we can always move from one view to another:\n```js\nlet arr8 = new Uint8Array([0, 1, 2, 3]);\n\n// another view on the same data\nlet arr16 = new Uint16Array(arr8.buffer);\n```\n\n\nHere's the list of typed arrays:\n\n- `Uint8Array`, `Uint16Array`, `Uint32Array` -- for integer numbers of 8, 16 and 32 bits.\n  - `Uint8ClampedArray` -- for 8-bit integers, \"clamps\" them on assignment (see below).\n- `Int8Array`, `Int16Array`, `Int32Array` -- for signed integer numbers (can be negative).\n- `Float32Array`, `Float64Array` -- for signed floating-point numbers of 32 and 64 bits.\n\n```warn header=\"No `int8` or similar single-valued types\"\nPlease note, despite of the names like `Int8Array`, there's no single-value type like `int`, or `int8` in JavaScript.\n\nThat's logical, as `Int8Array` is not an array of these individual values, but rather a view on `ArrayBuffer`.\n```\n\n### Out-of-bounds behavior\n\nWhat if we attempt to write an out-of-bounds value into a typed array? There will be no error. But extra bits are cut-off.\n\nFor instance, let's try to put 256 into `Uint8Array`. In binary form, 256 is `100000000` (9 bits), but `Uint8Array` only provides 8 bits per value, that makes the available range from 0 to 255.\n\nFor bigger numbers, only the rightmost (less significant) 8 bits are stored, and the rest is cut off:\n\n![](8bit-integer-256.svg)\n\nSo we'll get zero.\n\nFor 257, the binary form is `100000001` (9 bits), the rightmost 8 get stored, so we'll have `1` in the array:\n\n![](8bit-integer-257.svg)\n\nIn other words, the number modulo 2<sup>8</sup> is saved.\n\nHere's the demo:\n\n```js run\nlet uint8array = new Uint8Array(16);\n\nlet num = 256;\nalert(num.toString(2)); // 100000000 (binary representation)\n\nuint8array[0] = 256;\nuint8array[1] = 257;\n\nalert(uint8array[0]); // 0\nalert(uint8array[1]); // 1\n```\n\n`Uint8ClampedArray` is special in this aspect, its behavior is different. It saves 255 for any number that is greater than 255, and 0 for any negative number. That behavior is useful for image processing.\n\n## TypedArray methods\n\n`TypedArray` has regular `Array` methods, with notable exceptions.\n\nWe can iterate, `map`, `slice`, `find`, `reduce` etc.\n\nThere are few things we can't do though:\n\n- No `splice` -- we can't \"delete\" a value, because typed arrays are views on a buffer, and these are fixed, contiguous areas of memory. All we can do is to assign a zero.\n- No `concat` method.\n\nThere are two additional methods:\n\n- `arr.set(fromArr, [offset])` copies all elements from `fromArr` to the `arr`, starting at position `offset` (0 by default).\n- `arr.subarray([begin, end])` creates a new view of the same type from `begin` to `end` (exclusive). That's similar to `slice` method (that's also supported), but doesn't copy anything -- just creates a new view, to operate on the given piece of data.\n\nThese methods allow us to copy typed arrays, mix them, create new arrays from existing ones, and so on.\n\n\n\n## DataView\n\n[DataView](mdn:/JavaScript/Reference/Global_Objects/DataView) is a special super-flexible \"untyped\" view over `ArrayBuffer`. It allows to access the data on any offset in any format.\n\n- For typed arrays, the constructor dictates what the format is. The whole array is supposed to be uniform. The i-th number is `arr[i]`.\n- With `DataView` we access the data with methods like `.getUint8(i)` or `.getUint16(i)`. We choose the format at method call time instead of the construction time.\n\nThe syntax:\n\n```js\nnew DataView(buffer, [byteOffset], [byteLength])\n```\n\n- **`buffer`** -- the underlying `ArrayBuffer`. Unlike typed arrays, `DataView` doesn't create a buffer on its own. We need to have it ready.\n- **`byteOffset`** -- the starting byte position of the view (by default 0).\n- **`byteLength`** -- the byte length of the view (by default till the end of `buffer`).\n\nFor instance, here we extract numbers in different formats from the same buffer:\n\n```js run\n// binary array of 4 bytes, all have the maximal value 255\nlet buffer = new Uint8Array([255, 255, 255, 255]).buffer;\n\nlet dataView = new DataView(buffer);\n\n// get 8-bit number at offset 0\nalert( dataView.getUint8(0) ); // 255\n\n// now get 16-bit number at offset 0, it consists of 2 bytes, together interpreted as 65535\nalert( dataView.getUint16(0) ); // 65535 (biggest 16-bit unsigned int)\n\n// get 32-bit number at offset 0\nalert( dataView.getUint32(0) ); // 4294967295 (biggest 32-bit unsigned int)\n\ndataView.setUint32(0, 0); // set 4-byte number to zero, thus setting all bytes to 0\n```\n\n`DataView` is great when we store mixed-format data in the same buffer. For example, when we store a sequence of pairs (16-bit integer, 32-bit float), `DataView` allows to access them easily.\n\n## Summary\n\n`ArrayBuffer` is the core object, a reference to the fixed-length contiguous memory area.\n\nTo do almost any operation on `ArrayBuffer`, we need a view.\n\n- It can be a `TypedArray`:\n    - `Uint8Array`, `Uint16Array`, `Uint32Array` -- for unsigned integers of 8, 16, and 32 bits.\n    - `Uint8ClampedArray` -- for 8-bit integers, \"clamps\" them on assignment.\n    - `Int8Array`, `Int16Array`, `Int32Array` -- for signed integer numbers (can be negative).\n    - `Float32Array`, `Float64Array` -- for signed floating-point numbers of 32 and 64 bits.\n- Or a `DataView` -- the view that uses methods to specify a format, e.g. `getUint8(offset)`.\n\nIn most cases we create and operate directly on typed arrays, leaving `ArrayBuffer` under cover, as a \"common denominator\". We can access it as `.buffer` and make another view if needed.\n\nThere are also two additional terms, that are used in descriptions of methods that operate on binary data:\n- `ArrayBufferView` is an umbrella term for all these kinds of views.\n- `BufferSource` is an umbrella term for `ArrayBuffer` or `ArrayBufferView`.\n\nWe'll see these terms in the next chapters. `BufferSource` is one of the most common terms, as it means \"any kind of binary data\" -- an `ArrayBuffer` or a view over it.\n\nHere's a cheatsheet:\n\n![](arraybuffer-view-buffersource.svg)\n"
  },
  {
    "path": "4-binary/02-text-decoder/article.md",
    "content": "# TextDecoder and TextEncoder\n\nWhat if the binary data is actually a string? For instance, we received a file with textual data.\n\nThe build-in [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder) object allows to read the value into an actual JavaScript string, given the buffer and the encoding.\n\nWe first need to create it:\n```js\nlet decoder = new TextDecoder([label], [options]);\n```\n\n- **`label`** -- the encoding, `utf-8` by default, but `big5`, `windows-1251` and many other are also supported.\n- **`options`** -- optional object:\n  - **`fatal`** -- boolean, if `true` then throw an exception for invalid (non-decodable) characters, otherwise (default) replace them with character `\\uFFFD`.\n  - **`ignoreBOM`** -- boolean, if `true` then ignore BOM (an optional byte-order Unicode mark), rarely needed.\n\n...And then decode:\n\n```js\nlet str = decoder.decode([input], [options]);\n```\n\n- **`input`** -- `BufferSource` to decode.\n- **`options`** -- optional object:\n  - **`stream`** -- true for decoding streams, when `decoder` is called repeatedly with incoming chunks of data. In that case a multi-byte character may occasionally split between chunks. This options tells `TextDecoder` to memorize \"unfinished\" characters and decode them when the next chunk comes.\n\nFor instance:\n\n```js run\nlet uint8Array = new Uint8Array([72, 101, 108, 108, 111]);\n\nalert( new TextDecoder().decode(uint8Array) ); // Hello\n```\n\n\n```js run\nlet uint8Array = new Uint8Array([228, 189, 160, 229, 165, 189]);\n\nalert( new TextDecoder().decode(uint8Array) ); // 你好\n```\n\nWe can decode a part of the buffer by creating a subarray view for it:\n\n\n```js run\nlet uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]);\n\n// the string is in the middle\n// create a new view over it, without copying anything\nlet binaryString = uint8Array.subarray(1, -1);\n\nalert( new TextDecoder().decode(binaryString) ); // Hello\n```\n\n## TextEncoder\n\n[TextEncoder](https://encoding.spec.whatwg.org/#interface-textencoder) does the reverse thing -- converts a string into bytes.\n\nThe syntax is:\n\n```js\nlet encoder = new TextEncoder();\n```\n\nThe only encoding it supports is \"utf-8\".\n\nIt has two methods:\n- **`encode(str)`** -- returns `Uint8Array` from a string.\n- **`encodeInto(str, destination)`** -- encodes `str` into `destination` that must be `Uint8Array`.\n\n```js run\nlet encoder = new TextEncoder();\n\nlet uint8Array = encoder.encode(\"Hello\");\nalert(uint8Array); // 72,101,108,108,111\n```\n"
  },
  {
    "path": "4-binary/03-blob/article.md",
    "content": "# Blob\n\n`ArrayBuffer` and views are a part of ECMA standard, a part of JavaScript.\n\nIn the browser, there are additional higher-level objects, described in [File API](https://www.w3.org/TR/FileAPI/), in particular `Blob`.\n\n`Blob` consists of an optional string `type` (a MIME-type usually), plus `blobParts` -- a sequence of other `Blob` objects, strings and `BufferSource`.\n\n![](blob.svg)\n\nThe constructor syntax is:\n\n```js\nnew Blob(blobParts, options);\n```\n\n- **`blobParts`** is an array of `Blob`/`BufferSource`/`String` values.\n- **`options`** optional object:\n  - **`type`** -- `Blob` type, usually MIME-type, e.g. `image/png`,\n  - **`endings`** -- whether to transform end-of-line to make the `Blob` correspond to current OS newlines (`\\r\\n` or `\\n`). By default `\"transparent\"` (do nothing), but also can be `\"native\"` (transform).\n\nFor example:\n\n```js\n// create Blob from a string\nlet blob = new Blob([\"<html>…</html>\"], {type: 'text/html'});\n// please note: the first argument must be an array [...]\n```\n\n```js\n// create Blob from a typed array and strings\nlet hello = new Uint8Array([72, 101, 108, 108, 111]); // \"Hello\" in binary form\n\nlet blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});\n```\n\n\nWe can extract `Blob` slices with:\n\n```js\nblob.slice([byteStart], [byteEnd], [contentType]);\n```\n\n- **`byteStart`** -- the starting byte, by default 0.\n- **`byteEnd`** -- the last byte (exclusive, by default till the end).\n- **`contentType`** -- the `type` of the new blob, by default the same as the source.\n\nThe arguments are similar to `array.slice`, negative numbers are allowed too.\n\n```smart header=\"`Blob` objects are immutable\"\nWe can't change data directly in a `Blob`, but we can slice parts of a `Blob`, create new `Blob` objects from them, mix them into a new `Blob` and so on.\n\nThis behavior is similar to JavaScript strings: we can't change a character in a string, but we can make a new corrected string.\n```\n\n## Blob as URL\n\nA Blob can be easily used as a URL for `<a>`, `<img>` or other tags, to show its contents.\n\nThanks to `type`, we can also download/upload `Blob` objects, and the `type` naturally becomes `Content-Type` in network requests.\n\nLet's start with a simple example. By clicking on a link you download a dynamically-generated `Blob` with `hello world` contents as a file:\n\n```html run\n<!-- download attribute forces the browser to download instead of navigating -->\n<a download=\"hello.txt\" href='#' id=\"link\">Download</a>\n\n<script>\nlet blob = new Blob([\"Hello, world!\"], {type: 'text/plain'});\n\nlink.href = URL.createObjectURL(blob);\n</script>\n```\n\nWe can also create a link dynamically in JavaScript and simulate a click by `link.click()`, then download starts automatically.\n\nHere's the similar code that causes user to download the dynamically created `Blob`, without any HTML:\n\n```js run\nlet link = document.createElement('a');\nlink.download = 'hello.txt';\n\nlet blob = new Blob(['Hello, world!'], {type: 'text/plain'});\n\nlink.href = URL.createObjectURL(blob);\n\nlink.click();\n\nURL.revokeObjectURL(link.href);\n```\n\n`URL.createObjectURL` takes a `Blob` and creates a unique URL for it, in the form `blob:<origin>/<uuid>`.\n\nThat's what the value of `link.href` looks like:\n\n```\nblob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273\n```\n\nFor each URL generated by `URL.createObjectURL` the browser stores a URL -> `Blob` mapping internally. So such URLs are short, but allow to access the `Blob`.\n\nA generated URL (and hence the link with it) is only valid within the current document, while it's open. And it allows to reference the `Blob` in `<img>`, `<a>`, basically any other object that expects a URL.\n\nThere's a side-effect though. While there's a mapping for a `Blob`, the `Blob` itself resides in the memory. The browser can't free it.\n\nThe mapping is automatically cleared on document unload, so `Blob` objects are freed then. But if an app is long-living, then that doesn't happen soon.\n\n**So if we create a URL, that `Blob` will hang in memory, even if not needed any more.**\n\n`URL.revokeObjectURL(url)` removes the reference from the internal mapping, thus allowing the `Blob` to be deleted (if there are no other references), and the memory to be freed.\n\nIn the last example, we intend the `Blob` to be used only once, for instant downloading, so we call `URL.revokeObjectURL(link.href)` immediately.\n\nIn the previous example with the clickable HTML-link, we don't call `URL.revokeObjectURL(link.href)`, because that would make the `Blob` url invalid. After the revocation, as the mapping is removed, the URL doesn't work any more.\n\n## Blob to base64\n\nAn alternative to `URL.createObjectURL` is to convert a `Blob` into a base64-encoded string.\n\nThat encoding represents binary data as a string of ultra-safe \"readable\" characters with ASCII-codes from 0 to 64. And what's more important -- we can use this encoding in \"data-urls\".\n\nA [data url](mdn:/http/Data_URIs) has the form `data:[<mediatype>][;base64],<data>`. We can use such urls everywhere, on par with \"regular\" urls.\n\nFor instance, here's a smiley:\n\n```html\n<img src=\"data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7\">\n```\n\nThe browser will decode the string and show the image: <img src=\"data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7\">\n\n\nTo transform a `Blob` into base64, we'll use the built-in `FileReader` object. It can read data from Blobs in multiple formats. In the [next chapter](info:file) we'll cover it more in-depth.\n\nHere's the demo of downloading a blob, now via base-64:\n\n```js run\nlet link = document.createElement('a');\nlink.download = 'hello.txt';\n\nlet blob = new Blob(['Hello, world!'], {type: 'text/plain'});\n\n*!*\nlet reader = new FileReader();\nreader.readAsDataURL(blob); // converts the blob to base64 and calls onload\n*/!*\n\nreader.onload = function() {\n  link.href = reader.result; // data url\n  link.click();\n};\n```\n\nBoth ways of making a URL of a `Blob` are usable. But usually `URL.createObjectURL(blob)` is simpler and faster.\n\n```compare title-plus=\"URL.createObjectURL(blob)\" title-minus=\"Blob to data url\"\n+ We need to revoke them if care about memory.\n+ Direct access to blob, no \"encoding/decoding\"\n- No need to revoke anything.\n- Performance and memory losses on big `Blob` objects for encoding.\n```\n\n## Image to blob\n\nWe can create a `Blob` of an image, an image part, or even make a page screenshot. That's handy to upload it somewhere.\n\nImage operations are done via `<canvas>` element:\n\n1. Draw an image (or its part) on canvas using [canvas.drawImage](mdn:/api/CanvasRenderingContext2D/drawImage).\n2. Call canvas method [.toBlob(callback, format, quality)](mdn:/api/HTMLCanvasElement/toBlob) that creates a `Blob` and runs `callback` with it when done.\n\nIn the example below, an image is just copied, but we could cut from it, or transform it on canvas prior to making a blob:\n\n```js run\n// take any image\nlet img = document.querySelector('img');\n\n// make <canvas> of the same size\nlet canvas = document.createElement('canvas');\ncanvas.width = img.clientWidth;\ncanvas.height = img.clientHeight;\n\nlet context = canvas.getContext('2d');\n\n// copy image to it (this method allows to cut image)\ncontext.drawImage(img, 0, 0);\n// we can context.rotate(), and do many other things on canvas\n\n// toBlob is async operation, callback is called when done\ncanvas.toBlob(function(blob) {\n  // blob ready, download it\n  let link = document.createElement('a');\n  link.download = 'example.png';\n\n  link.href = URL.createObjectURL(blob);\n  link.click();\n\n  // delete the internal blob reference, to let the browser clear memory from it\n  URL.revokeObjectURL(link.href);\n}, 'image/png');\n```\n\nIf we prefer `async/await` instead of callbacks:\n```js\nlet blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n```\n\nFor screenshotting a page, we can use a library such as <https://github.com/niklasvh/html2canvas>. What it does is just walks the page and draws it on `<canvas>`. Then we can get a `Blob` of it the same way as above.\n\n## From Blob to ArrayBuffer\n\nThe `Blob` constructor allows to create a blob from almost anything, including any `BufferSource`.\n\nBut if we need to perform low-level processing, we can get the lowest-level `ArrayBuffer` from it using `FileReader`:\n\n```js\n// get arrayBuffer from blob\nlet fileReader = new FileReader();\n\n*!*\nfileReader.readAsArrayBuffer(blob);\n*/!*\n\nfileReader.onload = function(event) {\n  let arrayBuffer = fileReader.result;\n};\n```\n\n\n## Summary\n\nWhile `ArrayBuffer`, `Uint8Array` and other `BufferSource` are \"binary data\", a [Blob](https://www.w3.org/TR/FileAPI/#dfn-Blob) represents \"binary data with type\".\n\nThat makes Blobs convenient for upload/download operations, that are so common in the browser.\n\nMethods that perform web-requests, such as [XMLHttpRequest](info:xmlhttprequest), [fetch](info:fetch) and so on, can work with `Blob` natively, as well as with other binary types.\n\nWe can easily convert between `Blob` and low-level binary data types:\n\n- We can make a Blob from a typed array using `new Blob(...)` constructor.\n- We can get back `ArrayBuffer` from a Blob using `FileReader`, and then create a view over it for low-level binary processing.\n"
  },
  {
    "path": "4-binary/04-file/article.md",
    "content": "# File dan FileReader\n\nObjek [File](https://www.w3.org/TR/FileAPI/#dfn-file) diwariskan dari `Blob` dan di-*extend* dengan kapabilitas terkait sistem file.\n\nAda dua cara untuk menggunakannya.\n\nPertama, menggunakan konstruktor, mirip dengan `Blob`:\n\n```js\nnew File(fileParts, fileName, [options])\n```\n\n- **`fileParts`** -- adalah larik (array) dengan *value* Blob/BufferSource/String.\n- **`fileName`** -- string nama file.\n- **`options`** -- objek opsional:\n    - **`lastModified`** -- *timestamp* (tanggal *integer*) dari modifikasi terakhir.\n\nKedua, lebih sering kita mendapatkan file dari `<input type=\"file\">` atau *drag'n'drop* atau antarmuka peramban lainnya. Dalam hal ini, file mendapatkan informasi ini dari OS.\n\nKarena `File` diwariskan dari `Blob`, objek `File` memiliki properti yang sama, dengan tambahan:\n- `name` -- nama file,\n- `lastModified` -- *timestamp* modifikasi terakhir.\n\nBeginilah cara kita mendapatkan objek `File` dari `<input type=\"file\">`:\n\n```html run\n<input type=\"file\" onchange=\"showFile(this)\">\n\n<script>\nfunction showFile(input) {\n  let file = input.files[0];\n\n  alert(`File name: ${file.name}`); // e.g my.png\n  alert(`Last modified: ${file.lastModified}`); // e.g 1552830408824\n}\n</script>\n```\n\n```smart\nInput memungkinkan untuk memilih beberapa file, jadi `input.files` adalah sebuah objek yang seperti array. Di sini kita hanya memiliki satu file, jadi kita hanya mengambil `input.files[0]`.\n```\n\n## FileReader\n\n[FileReader](https://www.w3.org/TR/FileAPI/#dfn-filereader) adalah objek dengan tujuan tunggal untuk membaca data dari objek `Blob` (dan karenanya `File` juga).\n\nFileReader mengirimkan data menggunakan *event*, karena membaca dari *disk* mungkin memakan waktu.\n\nKonstruktor:\n\n```js\nlet reader = new FileReader(); // tanpa argumen\n```\n\n*Method* utama:\n\n- **`readAsArrayBuffer(blob)`** -- membaca data dalam format *binary* `ArrayBuffer`.\n- **`readAsText(blob, [encoding])`** -- membaca data sebagai string teks dengan *encoding* yang diberikan (`utf-8` secara default).\n- **`readAsDataURL(blob)`** -- membaca data *binary* dan menyandikannya sebagai url data base64.\n- **`abort()`** -- membatalkan operasi.\n\nPilihan *method* `read*` bergantung pada format yang kita inginkan, bagaimana kita akan menggunakan datanya.\n\n- `readAsArrayBuffer` -- untuk file *binary*, untuk melakukan operasi *binary* tingkat rendah. Untuk operasi tingkat tinggi, seperti *slicing*, `File` mewarisi dari `Blob`, sehingga kita dapat memanggilnya secara langsung, tanpa membacanya.\n- `readAsText` -- untuk file teks, ketika kita ingin mendapatkan string.\n- `readAsDataURL` -- ketika kita ingin menggunakan data ini di `src` untuk `img` atau tag lainnya. Ada alternatif untuk membaca file untuk itu, seperti yang dibahas dalam bab <info:blob>: `URL.createObjectURL(file)`.\n\nSaat pembacaan file berlangsung, ada *event*:\n- `loadstart` -- pemuatan dimulai.\n- `progress` -- terjadi selama membaca file.\n- `load` -- tidak ada kesalahan, membaca file selesai.\n- `abort` -- `abort()` dipanggil.\n- `error` -- terjadi kesalahan.\n- `loadend` -- membaca file selesai, baik sukses ataupun gagal.\n\nKetika pembacaan  file selesai, kita dapat mengakses hasilnya sebagai:\n- `reader.result` adalah hasilnya (jika berhasil)\n- `reader.error` adalah kesalahan (jika gagal).\n\nPeristiwa yang pasti paling banyak digunakan adalah `load` dan `error`.\n\nBerikut ini contoh membaca sebuah file:\n\n```html run\n<input type=\"file\" onchange=\"readFile(this)\">\n\n<script>\nfunction readFile(input) {\n  let file = input.files[0];\n\n  let reader = new FileReader();\n\n  reader.readAsText(file);\n\n  reader.onload = function() {\n    console.log(reader.result);\n  };\n\n  reader.onerror = function() {\n    console.log(reader.error);\n  };\n\n}\n</script>\n```\n\n```smart header=\"`FileReader` untuk blobs\"\nSeperti disebutkan dalam bab <info:blob>, `FileReader` tidak hanya dapat membaca file, tetapi semua blob.\n\nKita dapat menggunakannya untuk mengonversi blob ke dalam format yang lain:\n- `readAsArrayBuffer(blob)` -- ke `ArrayBuffer`,\n- `readAsText(blob, [encoding])` -- ke string (alternatif untuk `TextDecoder`),\n- `readAsDataURL(blob)` -- ke url data base64.\n```\n\n\n```smart header=\"`FileReaderSync` tersedia di dalam Web Workers\"\nUntuk Web Workers, ada juga sebuah varian synchronous dari `FileReader`, yang disebut [FileReaderSync](https://www.w3.org/TR/FileAPI/#FileReaderSync).\n\nMethod pembacaannya `read*` tidak menghasilkan event, melainkan mengembalikan hasil, seperti yang dilakukan fungsi biasa.\n\nFileReaderSync hanya di dalam Web Worker, karena penundaan dalam panggilan synchronous, yang memungkinkan saat membaca dari file, di Web Worker kurang penting. Mereka tidak mempengaruhi halaman.\n```\n\n## Ringkasan\n\nObjek `File` diwariskan dari `Blob`.\n\nSelain *method* dan properti seperti `Blob`, objek `File` juga memiliki properti `name` dan `lastModified`, ditambah kemampuan internal untuk membaca dari sistem file. Kita biasanya mendapatkan objek `File` dari input pengguna, seperti *event* `<input>` atau Drag'n'Drop (`ondragend`).\n\nObjek `FileReader` dapat membaca dari file atau blob, dalam salah satu dari tiga format berikut:\n- String (`readAsText`).\n- `ArrayBuffer` (`readAsArrayBuffer`).\n- Url data, di-*encode* ke base-64 (`readAsDataURL`).\n\nNamun, dalam banyak kasus, kita tidak perlu membaca konten file. Seperti yang kita lakukan dengan blob, kita dapat membuat url pendek dengan `URL.createObjectURL(file)` dan menetapkannya ke `<a>` atau `<img>`. Dengan cara ini file dapat diunduh atau ditampilkan sebagai gambar, sebagai bagian dari kanvas, dll.\n\nDan jika kita akan mengirim `File` melalui jaringan, itu juga mudah: API jaringan seperti `XMLHttpRequest` atau `fetch` secara *native* menerima objek `File`.\n"
  },
  {
    "path": "4-binary/index.md",
    "content": "# Binary data, files\n\nWorking with binary data and files in JavaScript.\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/solution.js",
    "content": "\nasync function getUsers(names) {\n  let jobs = [];\n\n  for(let name of names) {\n    let job = fetch(`https://api.github.com/users/${name}`).then(\n      successResponse => {\n        if (successResponse.status != 200) {\n          return null;\n        } else {\n          return successResponse.json();\n        }\n      },\n      failResponse => {\n        return null;\n      }\n    );\n    jobs.push(job);\n  }\n\n  let results = await Promise.all(jobs);\n\n  return results;\n}\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/source.js",
    "content": "\nasync function getUsers(names) {\n  /* your code */\n}\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/_js.view/test.js",
    "content": "describe(\"getUsers\", function() {\n\n  it(\"gets users from GitHub\", async function() {\n    let users = await getUsers(['iliakan', 'remy', 'no.such.users']);\n    assert.equal(users[0].login, 'iliakan');\n    assert.equal(users[1].login, 'remy');\n    assert.equal(users[2], null);\n  });\n\n});\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/solution.md",
    "content": "Untuk mengambil pengguna kita membutuhkan : `fetch('https://api.github.com/users/USERNAME')`.\n\nJika tanggapan memiliki status `200` panggil `.json()` untuk membaca objek JSON.\n\nSebaliknya, jika sebuah `fetch` gagal, atau tanggapan tidak memiliki status 200, kita akan mengembalikan `null` di senarai hasilnya.\n\nJadi ini kodenya : \n\n```js demo\nasync function getUsers(names) {\n  let jobs = [];\n\n  for(let name of names) {\n    let job = fetch(`https://api.github.com/users/${name}`).then(\n      successResponse => {\n        if (successResponse.status != 200) {\n          return null;\n        } else {\n          return successResponse.json();\n        }\n      },\n      failResponse => {\n        return null;\n      }\n    );\n    jobs.push(job);\n  }\n\n  let results = await Promise.all(jobs);\n\n  return results;\n}\n```\n\nPerlu dicatat, panggilan `.then()` terpasang secara langsung dengan `fetch`, jadi saat kita memiliki tanggapan, tidak akan menunggu untuk *fetch* lainnya, tetapi segera mulai membaca `.json()`. \n\nJika kita menggunakan `await Promise.all(names.map(name => fetch(...)))`, dan memanggil `.json()` di hasilnya, maka akan menunggu semua *fetch* untuk menanggapi. Dengan menambahkan `.json()` untuk masing masing *fetch*, kita dapat memastikan pemanggilan *fetch* individu mulai membaca data sebagai JSON tanpa menunggu satu sama lain.\n\nIni adalah contoh bagaimana *API Promise* tingkat rendah masih bisa digunakan meskipun kita utamanya menggunakan `async / await`.\n"
  },
  {
    "path": "5-network/01-fetch/01-fetch-users/task.md",
    "content": "# Fetch pengguna dari GitHub\n\nBuatlah sebuah fungsi *async* `getUsers(names)`, yang akan mendapatkan sebuah senarai dari Github *logins*, ambil pengguna dari Github dan kembalikan sebuah senarai dari pengguna GitHub.\n\n*Url* Github dengan informasi pengguna yang diberikan nama `USERNAME` : `https://api.github.com/users/USERNAME`.\n\nini adalah contoh tes di sandbox.\n\nDetail Penting:\n\n1. Harus ada satu permintaan `fetch` untuk setiap pengguna.\n2. Permintaan tidak boleh menunggu satu sama lain. Sehingga datanya sampai secepatnya.\n3. Jika terdapat permintaan yang gagal, atau pengguna tidak ditemukan, fungsi tersebut harus mengembalikan `null` dalam senarai yang dihasilkan.\n"
  },
  {
    "path": "5-network/01-fetch/article.md",
    "content": "\n# Fetch\n\nJavascript bisa mengirim permintaan jaringan ke server dan memuat informasi baru saat dibutuhkan.\n\nSebagai contoh, kita bisa menggunakan permintaan jaringan untuk:\n\n- Mengirimkan sebuah pesanan.\n- Memuat informasi pengguna.\n- Menerima pembaharuan terbaru dari server.\n- Dan lain sebagainya.\n\n...Dan semua itu tanpa memuat ulang halaman!\n\nAda istilah *umbrella* AJAX(singkatan dari <b>A</b>synchronous <b>J</b>avaScript <b>A</b>nd <b>X</b>ML) untuk permintaan jaringan dari Javascript.\n\nMeskipun kita tidak perlu menggunakan XML : istilah ini berasal dari masa lalu, itulah kenapa kalimat ini ada. Kamu mungkin sudah mendengar istilah ini.\n\nAda banyak cara untuk mengirimkan sebuah permintaan jaringan dan mendapatkan informasi dari server. \n\nMetode `fetch()` itu baru dan serba guna, jadi kita bisa mulai dari sini. metode ini tidak di dukung oleh browser lama (bisa memakai polyfill), tetapi sudah didukung oleh browser yang terbaru.\n\nsintaksis dasarnya :\n\n```js\nlet promise = fetch(url, [options])\n```\n\n- **`url`** -- *URL* yang diakses.\n- **`options`** -- parameter opsional : metode, *headers* dll.\n\nJika tidak menggunakan `options`, dan hanya menggunakan permintaan *GET* sederhana,sudah bisa mengunduh isi dari `url`.\n\n*Browser* segera memulai permintaan dan mengembalikan sebuah *promise* yang memanggil kode yang akan di pakai untuk mendapatkan hasil.\n\nUntuk mendapatkan sebuah tanggapan biasanya ada dua tahap proses.\n\n**Pertama, *`promise`*, akan dikembalikan oleh `fetch`, diselesaikan dengan sebuah objek *built-in* [Response](https://fetch.spec.whatwg.org/#response-kelas) kelas secepat server menanggapi *headers***\n\n\nDi tahap ini kita bisa memeriksa status HTTP, untuk melihat apakah berhasil atau tidak, untuk memeriksa *headers*, tetapi belum memakai *body*.\n\n*Promise* tidak akan dijalankan jika `fetch` tidak  bisa membuat  permintaan HTTP, contohnya saat ada masalah jaringan, atau tidak ada situs yang di arahkan. Status HTTP yang tidak normal, seperti 404 atau 500 tidak menimbulkan kesalahan.\n\nKita bisa melihat status HTTP di properti tanggapan:\n\n- **`status`** -- kode status misalnya 200.\n- **`ok`** -- boolean, 'true' jika kode status HTTP 200-299.\n\nSebagai contoh :\n\n```js\nlet response = await fetch(url);\n\nif (response.ok) { // jika kode status 200-299 \n  //dapatkan tanggapan *body* (caranya akan di jelaskan di bawah)\n  let json = await response.json();\n} else {\n  alert(\"HTTP-Error: \" + response.status);\n}\n```\n\n**Kedua, untuk mendapatkan tanggapan *body* , kita perlu menggunakan metode pemanggilan tambahan**\n\n`Response` menyediakan banyak metode berbasis *promise* untuk mengakses *body* dengan berbagai format:\n\n- **`response.text()`** -- membaca tanggapan dan mengembalikan sebagai teks,\n- **`response.json()`** -- mengurai tanggapan sebagai objek JSON,\n- **`response.formData()`** -- mengembalikan tanggapan sebagai objek `FormData`(dijelaskan di [bab selanjutnya](info:formdata)),\n- **`response.blob()`** -- mengembalikan tanggapan sebagai [Blob](info:blob) (tipe untuk data biner),\n- **`response.ArrayBuffer()`** -- mengembalikan tanggapan sebagai [ArrayBuffer](info:Arraybuffer-binary-Arrays) (representasi tingkat rendah dari data biner),\n- Sebagai tambahan, `response.body` adalah objek  [ReadableStream](https://streams.spec.whatwg.org/#rs-kelas), hal ini  memungkinkan untuk membaca bagian  demi bagian *body*, kita akan melihat contohnya nanti.\n\nContohnya, mari kita dapatkan objek JSON terbaru dari commit GitHub:\n\n```js run async\nlet url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits';\nlet response = await fetch(url);\n\n*!*\nlet commits = await response.json(); //membaca tanggapan *body* dan menguraikan sebagai JSON\n*/!*\n\nalert(commits[0].author.login);\n```\n\nAtau, sama saja tanpa `await` , menggunakan sintaksis asli *promise*:\n\n```js run\nfetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits')\n  .then(response => response.json())\n  .then(commits => alert(commits[0].author.login));\n```\n\nUntuk mendapatkan data respon dengan format teks, gunakan `await response.text()` daripada `.json()`:\n\n```js run async\nlet response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');\n\nlet text = await response.text(); //membaca tanggapan *body* sebagai teks\n\nalert(text.slice(0, 80) + '...');\n```\n\nSebagai contoh kasus untuk membaca dalam format biner, mari kita ambil dan menunjukkan gambar logo [spesifikasi \"fetch\"](https://fetch.spec.whatwg.org) (lihat bab [Blob](info:blob) untuk detail tentang operasi di `Blob`):\n\n```js async run\nlet response = await fetch('/article/fetch/logo-fetch.svg');\n\n*!*\nlet blob = await response.blob(); //mengunduh sebagai objek Blob\n*/!*\n\n//membuat elemen <img> \nlet img = document.createElement('img');\nimg.style = 'position:fixed;top:10px;left:10px;width:100px';\ndocument.body.append(img);\n\n//menampilkan item\nimg.src = URL.createObjectURL(blob);\n\nsetTimeout(() => { //menyembunyikan setelah 3 detik\n  img.remove();\n  URL.revokeObjectURL(img.src);\n}, 3000);\n```\n\n````warn\nKita hanya bisa memilih satu dari beberapa metode untuk membaca *body*. \n\nJika kita sudah mendapatkan tanggapan dengan `response.text()` , maka `response.json()` tidak akan bekerja, karena isi *body* sudah di proses lebih awal.\n\n```js\nlet text = await response.text(); //tanggapan *body* sudah di konsumsi\nlet parsed = await response.json(); //gagal (sudah di konsumsi)\n```\n````\n\n## Response headers\n\nTanggapan headers tersedia seperti objek *Map headers* di `response.headers`.\n\nIni tidak seperti *Map*, tetapi memiliki metode yang mirip untuk mendapatkan *headers* individu dengan nama atau mengulangi item:\n\n```js run async\nlet response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits');\n\n//dapatkan satu header\nalert(response.headers.get('Content-Type')); // application/json; charset=utf-8\n\n//mengulangi semua header\nfor (let [key, value] of response.headers) {\n  alert(`${key} = ${value}`);\n}\n```\n\n## Response headers\n\nUntuk mengatur permintaan header di `fetch` , kita bisa menggunakan `headers` *option* yang memiliki sebuah objek dengan *headers* diluar, seperti ini:\n\n```js\nlet response = fetch(protectedUrl, {\n  headers: {\n    Authentication: 'secret'\n  }\n});\n```\n\n...Tetapi ada daftar [headers HTTP yang di larang](https://fetch.spec.whatwg.org/#forbidden-header-name) yang tidak boleh kita gunakan : \n\n- `Accept-Charset`, `Accept-Encoding`\n- `Access-Control-Request-Headers`\n- `Access-Control-Request-Method`\n- `Connection`\n- `Content-Length`\n- `Cookie`, `Cookie2`\n- `Date`\n- `DNT`\n- `Expect`\n- `Host`\n- `Keep-Alive`\n- `Origin`\n- `Referer`\n- `TE`\n- `Trailer`\n- `Transfer-Encoding`\n- `Upgrade`\n- `Via`\n- `Proxy-*`\n- `Sec-*`\n\nHeader ini memastikan HTTP yang tepat dan aman, jadi mereka khusus dikendalikan oleh browser.\n\n## POST Request\n\nUntuk membuat sebuah permintaan `POST` , atau sebuah permintaan dengan metode lain, kita perlu menggunakan *option* `fetch`: \n\n- **`method`** -- metode HTTP, misalnya `POST`,\n- **`body`** -- permintaan *body*, satu dari:\n  - sebuah string (misalnya dikodekan JSON),\n  - objek `FormData`, untuk mengirimkan data sebagai `form/multipart`,\n  - `Blob`/`BufferSource` untuk mengirimkan data biner, \n  - [URLSearchParams](info:url), untuk mengirimkan data di `x-www-form-urlencoded` pengkodean, jarang digunakan.\n\nFormat JSON adalah yang paling banyak digunakan sepanjang waktu.\n\nContohnya, kode ini mengirimkan objek `user` sebagai JSON:\n\n```js run async\nlet user = {\n  name: 'John',\n  surname: 'Smith'\n};\n\n*!*\nlet response = await fetch('/article/fetch/post/user', {\n  method: 'POST',\n  headers: {\n    'Content-Type': 'application/json;charset=utf-8'\n  },\n  body: JSON.stringify(user)\n});\n*/!*\n\nlet result = await response.json();\nalert(result.message);\n```\n\nPerlu dicatat, jika permintaan *`body`* berupa string, maka *header* `Content-Type` akan di atur `text/plain;charset=UTF-8` sebagai default.\n\nTetapi, karena kita akan mengirim JSON kita menggunakan pilihan `headers` untuk mengirim `application/json`, `Content-Type` sebagai jenis data yang benar untuk data pengkodean JSON.\n\n## Mengirim sebuah gambar\n\nKita juga bisa mengirimkan data biner dengan `fetch` menggunakan objek `Blob` atau `BufferSource`.\n\nDi contoh ini, ada `<canvas>` dimana kita bisa menggambar dengan memindahkan mouse diatasnya. Setiap klik di tombol \"*submit*\" mengirimkan gambar ke *server*:\n\n```html run autorun height=\"90\"\n<body style=\"margin:0\">\n  <canvas id=\"canvasElem\" width=\"100\" height=\"80\" style=\"border:1px solid\"></canvas>\n\n  <input type=\"button\" value=\"Submit\" onclick=\"submit()\">\n\n  <script>\n    canvasElem.onmousemove = function(e) {\n      let ctx = canvasElem.getContext('2d');\n      ctx.lineTo(e.clientX, e.clientY);\n      ctx.stroke();\n    };\n\n    async function submit() {\n      let blob = await new promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n      let response = await fetch('/article/fetch/post/image', {\n        method: 'POST',\n        body: blob\n      });\n\n      // *server* menanggapi dengan konfirmasi dan ukuran gambar\n      let result = await response.json();\n      alert(result.message);\n    }\n\n  </script>\n</body>\n```\n\nPerlu dicatat, disini kita tidak akan mengatur *header* `Content-Type` secara manual, karena sebuah objek `Blob` memiliki tipe *built-in* (`image/png`, di hasilkan menjadi `toBlob`). Untuk tipe objek `Blob` menjadi nilai dari `Content-Type`.\n\nFungsi `submit()` bisa ditulis ulang tanpa `async/await` seperti ini:\n\n```js\nfunction submit() {\n  canvasElem.toBlob(function(blob) {        \n    fetch('/article/fetch/post/image', {\n      method: 'POST',\n      body: blob\n    })\n      .then(response => response.json())\n      .then(result => alert(JSON.stringify(result, null, 2)))\n  }, 'image/png');\n}\n```\n\n## Ringkasan\n\nPermintaan fetch biasanya terdiri dari dua pemanggilan `await`:\n\n```js\nlet response = await fetch(url, options); //menyelesaikan dengan tanggapan header\nlet result = await response.json(); // membaca *body* sebagai json\n```\n\nAtau, tanpa `await`:\n\n```js\nfetch(url, options)\n  .then(response => response.json())\n  .then(result => /* hasil proses */)\n```\n\nProperti tanggapan:\n- `response.status` -- kode dari tanggapan HTTP,\n- `response.ok` -- `true` jika status 200-299.\n- `response.headers` -- seperti objek Map dengan headers HTTP.\n\nMetode untuk mendapatkan tanggapan *body*:\n- **`response.text()`** -- mengembalikan tanggapan sebagai teks,\n- **`response.json()`** -- menguraikan tanggapan sebagai objek JSON,\n- **`response.formData()`** -- mengembalikan tanggapan sebagai objek `FormData` ( pengkodean form/multipart , lihat bab selanjutnya),\n- **`response.blob()`** -- mengembalikan tanggapan sebagai [Blob](info:blob) (data biner dengan tipe),\n- **`response.ArrayBuffer()`** -- mengembalikan tanggapan sebagai [ArrayBuffer](info:Arraybuffer-binary-Arrays) (data biner tingkat rendah),\n\nOption Fetch sejauh ini:\n- `method` -- metode HTTP,\n- `headers` -- sebuah objek dengan permintaan *headers* (tidak semua *header* di izinkan),\n- `body` -- data untuk mengirimkan (permintaan *body*) sebagai objek `string`, `FormData`, `BufferSource`, `Blob` atau `UrlSearchParams`.\n\ndi bab selanjutnya kita akan melihat lebih banyak option dan kasus penggunaan `fetch`.\n"
  },
  {
    "path": "5-network/01-fetch/post.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\nconst bodyParser = require('koa-bodyparser');\nconst getRawBody = require('raw-body')\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.post('/user', async (ctx) => {\n  ctx.body = {\n    message: \"User saved.\"\n  };\n});\n\nrouter.post('/image', async (ctx) => {\n  let body = await getRawBody(ctx.req, {\n    limit: '1mb'\n  });\n  ctx.body = {\n    message: `Image saved, size:${body.length}.`\n  };\n});\n\napp\n  .use(bodyParser())\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/02-formdata/article.md",
    "content": "# FormData\n\nPada artikel ini akan membahas terkait pengiriman formulir HTML: dengan atau tanpa berkas, dengan ruas tambahan dan lainnya.\n\nObjek [FormData](https://xhr.spec.whatwg.org/#interface-formdata) dapat membantu untuk melakukan hal tersebut. seperti yang Anda duga, `FormData` adalah sebuah objek yang merepresentasikan data formulir HTML.\n\nKonstruktornya adalah:\n\n```js\nlet formData = new FormData([form]);\n```\n\nJika elemen `form` HTML tersedia, maka secara otomatis ruas yang ada akan dicatat.\n\nHal khusus tentang `FormData` adalah metode koneksinya. Seperti `fetch` yang dapat menerima objek `FormData` sebagai _request body_ kemudian dikodekan dan dikirim dengan format `Content-Type: multipart/form-data`.\n\nDari sudut pandang _server_, itu terlihat seperti pengiriman formulir biasa.\n\n## Mengirimkan formulir sederhana\n\nMari kita kirim formulir sederhana terlebih dahulu.\n\nSeperti yang Anda lihat, itu hampir hanya satu baris:\n\n```html run autorun\n<form id=\"formElem\">\n    <input type=\"text\" name=\"name\" value=\"John\" />\n    <input type=\"text\" name=\"surname\" value=\"Smith\" />\n    <input type=\"submit\" />\n</form>\n\n<script>\n      formElem.onsubmit = async (e) => {\n        e.preventDefault();\n\n        let response = await fetch('/article/formdata/post/user', {\n          method: 'POST',\n    *!*\n          body: new FormData(formElem)\n    */!*\n        });\n\n        let result = await response.json();\n\n        alert(result.message);\n      };\n</script>\n```\n\nPada contoh ini, kode _server_ tidak ditampilkan karena itu diluar dari cakupan pembahasan topik ini. _Server_ menerima _request_ POST dan memberikan balasan \"_User saved_\"\n\n## Metode FormData\n\nKita dapat memodifikasi ruas formulir di `FormData` dengan metode:\n\n-   `formData.append(nama, nilai)` - menambakan sebuah ruas formulir dengan `nama` dan `nilai` yang diberikan,\n-   `formData.append(nama, blob, namaBerkas)` - menambahkan sebuah ruas formulir seolah-olah itu adalah `<input type=\"file\">`, argumen ketiga `namaBerkas` digunakan untuk menetapkan nama berkas (bukan nama ruas formulir) seperti nama berkas yang ada di sistem berkas pengguna,\n-   `formData.delete(nama)` - menghapus ruas formulir dengan `nama` yang diberikan,\n-   `formData.get(nama)` - mendapatkan nilai dari ruas formulir dengan `nama` yang diberikan,\n-   `formData.has(nama)` - jika pada formulir terdapat ruas yang memiliki `nama` yang diberikan maka akan mengembalikan nilai `true`, jika tidak maka `false`.\n\nSebuah formulir secara teknis memperbolehkan memiliki banyak ruas formulir dengan nama yang sama, sehingga beberapa penggunaan `append` akan menambahkan lebih banyak ruas formulir dengan nama yang sama.\n\nTerdapat juga metode `set` dengan sintaks yang sama seperti `append`. Perbedaannya adalah `.set` menghapus semua ruas formulir dengan nama yang diberikan kemudian menambahkan ruas formulir baru. Jadi, itu memastikan bahwa hanya terdapat satu ruas formulir dengan nama itu, sisanya mirip seperti `append`:\n\n-   `formData.set(nama, nilai)`,\n-   `formData.set(nama, blob, namaBerkas)`.\n\nKita juga bisa dapat melakukan perulangan atas ruas _formData_ menggunakan `for ... of`:\n\n```js run\nlet formData = new FormData();\nformData.append('kunci1', 'nilai1');\nformData.append('kunci2', 'nilai2');\n\n// Daftar pasangan kunci/nilai\nfor (let [name, value] of formData) {\n    alert(`${name} = ${value}`); // kunci1=nilai1, maka kunci2=nilai2\n}\n```\n\n## Mengirimkan formulir dengan sebuah berkas\n\nFormulir selalu mengirimkan data sebagai `Content-Type: multipart/form-data`, pengkodean ini memperbolehkan pengiriman sebuah berkas. Jadi, ruas `<input type=\"file\">` juga dapat dikirim, mirip seperti pengiriman formulir biasa.\n\nBerikut adalah contoh formulir tersebut:\n\n```html run autorun\n<form id=\"formElem\">\n    <input type=\"text\" name=\"firstName\" value=\"John\" />\n    Picture: <input type=\"file\" name=\"picture\" accept=\"image/*\" />\n    <input type=\"submit\" />\n</form>\n\n<script>\n      formElem.onsubmit = async (e) => {\n        e.preventDefault();\n\n        let response = await fetch('/article/formdata/post/user-avatar', {\n          method: 'POST',\n    *!*\n          body: new FormData(formElem)\n    */!*\n        });\n\n        let result = await response.json();\n\n        alert(result.message);\n      };\n</script>\n```\n\n## Mengrimkan sebuah formulir dengan data Blob\n\nSeperti yang sudah kita lihat pada pembahasan <info:fetch>, dimana sangat mudah untuk mengirim data biner yang dihasilkan secara dinamis, misalnya sebuah gambar sebagai `Blob`. Kita dapat secara langsung menjadikannya sebagai parameter `body` `fetch`.\n\nMeskipun dalam praktinya, sering kali lebih mudah untuk mengirim gambar tidak secara terpisah tetapi sebagai bagian dari formulir dengan ruas tambahan seperti \"nama\" dan metadata lainnya.\n\nSelain itu, _server_ biasanya lebih cocok untuk menerima formulir yang dikodekan dengan `Content-Type: multipart/form-data` daripada data biner mentah.\n\nContoh di bawah ini mengirimkan gambar dari `<canvas>` bersama dengan ruas formulir lainnya menggunakan `FormData`:\n\n```html run autorun height=\"90\"\n<body style=\"margin:0\">\n    <canvas id=\"canvasElem\" width=\"100\" height=\"80\" style=\"border:1px solid\"></canvas>\n\n    <input type=\"button\" value=\"Submit\" onclick=\"submit()\" />\n\n    <script>\n            canvasElem.onmousemove = function(e) {\n              let ctx = canvasElem.getContext('2d');\n              ctx.lineTo(e.clientX, e.clientY);\n              ctx.stroke();\n            };\n\n            async function submit() {\n              let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));\n\n        *!*\n              let formData = new FormData();\n              formData.append(\"firstName\", \"John\");\n              formData.append(\"image\", imageBlob, \"image.png\");\n        */!*\n\n              let response = await fetch('/article/formdata/post/image-form', {\n                method: 'POST',\n                body: formData\n              });\n              let result = await response.json();\n              alert(result.message);\n            }\n    </script>\n</body>\n```\n\nPerhatikan bagaimana gambar `Blob` ditambahkan:\n\n```js\nformData.append('image', imageBlob, 'image.png');\n```\n\nItu seolah-olah seperti terdapat `<input type=\"file\" name=\"image\">` di dalam formulir kemudian pengunjung mengirimkan sebuah berkas dengan nama `\"image.png\"` (argumen ke-3) dengan data `\"imageBlob\"` (argumen ke-2) dari sistem berkasnya.\n\n_Server_ membaca data formulir dan berkas seolah-olah itu adalah pengajuan formulir biasa.\n\n## Ringkasan\n\nObjek [FormData](https://xhr.spec.whatwg.org/#interface-formdata) digunakan untuk mendapatkan formulir HTML dan mengirimkannya menggunakan `fetch` atau metode jaringan lainnya.\n\nKita dapat membuat `new FormData(form)` dari formulir HTML atau membuat sebuah objek `FormData` tanpa sebuah formulir HTML dan kemudian menambahkan ruas formulir dengan metode berikut:\n\n-   `formData.append(nama, nilai)`\n-   `formData.append(nama, blob, namaBerkas)`\n-   `formData.set(nama, nilai)`\n-   `formData.set(nama, blob, namaBerkas)`\n\nCoba perhatikan dua keunikan ini:\n\n1. Metode `set` menghapus ruas formulir dengan properti `name` yang sama tetapi `append` tidak. Itu adalah satu-satunya perbedaan diantara kedua metode tersebut.\n2. Untuk mengirimkan berkas, diperlukan sebanyak 3 sintaks argumen. Argumen terakhir adalah nama berkas yang normalnya didapatkan dari sistem berkas pengguna untuk `<input type=\"file\">`.\n\nMetode-metode lainnya adalah:\n\n-   `formData.delete(name)`\n-   `formData.get(name)`\n-   `formData.has(name)`\n\nMantap, itu sudah semuanya!\n"
  },
  {
    "path": "5-network/02-formdata/post.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\nconst bodyParser = require('koa-bodyparser');\nconst getRawBody = require('raw-body')\nconst busboy = require('async-busboy');\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.post('/user', async (ctx) => {\n  ctx.body = {\n    message: \"User saved\"\n  };\n});\n\nrouter.post('/image-form', async (ctx) => {\n\n  let files = [];\n  const { fields } = await busboy(ctx.req, {\n    onFile(fieldname, file, filename, encoding, mimetype) {\n      // read all file stream to continue\n      let length = 0;\n      file.on('data', function(data) {\n        length += data.length;\n      });\n      file.on('end', () => {\n        files.push({\n          fieldname,\n          filename,\n          length\n        });\n      });\n    }\n  });\n\n  ctx.body = {\n    message: `Image saved, firstName: ${fields.firstName}, Image size:${files[0].length}, fileName: ${files[0].filename}`\n  };\n});\n\n\nrouter.post('/user-avatar', async (ctx) => {\n\n  let files = [];\n  const { fields } = await busboy(ctx.req, {\n    onFile(fieldname, file, filename, encoding, mimetype) {\n      // read all file stream to continue\n      let length = 0;\n      file.on('data', function(data) {\n        length += data.length;\n      });\n      file.on('end', () => {\n        files.push({\n          fieldname,\n          filename,\n          length\n        });\n      });\n\n    }\n  });\n\n  ctx.body = {\n    message: `User with picture, firstName: ${fields.firstName}, picture size:${files[0].length}`\n  };\n});\n\napp\n  .use(bodyParser())\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/03-fetch-progress/article.md",
    "content": "# Fetch: Kemajuan Download\n\nMetode `fetch` memungkinkan untuk melacak kemajuan proses _download_.\n\nHarap diperhatikan: Saat ini belum ada cara bagi metode `fetch` untuk melacak kemajuan proses _upload_. Untuk tujuan tersebut, gunakan [XMLHttpRequest](info:xmlhttprequest), kita akan membahasnya nanti.\n\nUntuk melacak kemajuan _download_, kita dapat menggunakan properti `response.body`. Itu adalah `ReadableStream` -- sebuah objek khusus yang menyediakan potongan demi potongan `response.body` saat didapatkan. _Readable streams_ dideskripsikan pada spesifikasi [Streams API](https://streams.spec.whatwg.org/#rs-class).\n\nTidak seperti `response.text()`, `response.json()` dan metode lainnya, `response.body` memberikan kontrol penuh atas proses pembacaan dan kita dapat menghitung berapa banyak _data_ yang diterima setiap saat.\n\nBerikut adalah sketsa kode yang membaca respon dari `response.body`:\n\n```js\n// sebagai ganti response.json() dan metode lainnya\nconst reader = response.body.getReader();\n\n// perulangan tidak terbatas ketika proses mengunduh\nwhile (true) {\n  // done bernilai benar (true) untuk bagian terakhir\n  // value adalah Uint8Array dari bagian bytes\n  const { done, value } = await reader.read();\n\n  if (done) {\n    break;\n  }\n\n  console.log(`Diterima ${value.length} bytes`);\n}\n```\n\nHasil dari pemanggilan `await reader.read()` adalah objek dengan dua properti:\n\n- **`done`** -- `true` saat proses pembacaan selesai, jika tidak `false`.\n- **`value`** -- larik dengan tipe bytes: `Uint8Array`.\n\n```smart\nStreams API juga mendeskripsikan iterasi asynchronous melalui `ReadableStream` dengan perulangan `for await..of`, tetapi itu tidak sepenuhnya didukung (lihat [browser issues](https://github.com/whatwg/streams/issues/778#issuecomment-461341033)), sehingga kita menggunakan perulangan `while`.\n```\n\nKita menerima potongan respon pada perulangan sampai proses _loading_ selesai, yaitu: sampai `done` bernilai `true`.\n\nUntuk mencatat kemajuan, kita hanya perlu setiap `value` dari _fragment_ yang diterima untuk ditambahkan nilai panjangnya ke penghitung.\n\nBerikut adalah contoh penuh yang menerima respon dan mencatat kemajuan pada _console_, Langkah-langkah yang dapat diikuti:\n\n```js run async\n// Langkah 1: mulai fetch dan dapatkan reader\nlet response = await fetch(\n  'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100'\n);\n\nconst reader = response.body.getReader();\n\n// Langkah 2: dapatkan panjang total (data)\nconst contentLength = +response.headers.get('Content-Length');\n\n// Langkah 3: Membaca data\nlet receivedLength = 0; // Menerima banyak bytes saat ini\nlet chunks = []; // larik potongan biner yang diterima (Meliputi body respon)\nwhile (true) {\n  const { done, value } = await reader.read();\n\n  if (done) {\n    break;\n  }\n\n  chunks.push(value);\n  receivedLength += value.length;\n\n  console.log(`Diterima ${receivedLength} dari ${contentLength}`);\n}\n\n// Langkah 4: Menyatukan potongan-potongan menjadi satu Uint8Array\nlet chunksAll = new Uint8Array(receivedLength); // (4.1)\nlet position = 0;\nfor (let chunk of chunks) {\n  chunksAll.set(chunk, position); // (4.2)\n  position += chunk.length;\n}\n\n// Langkah 5: Mengubah menjadi string\nlet result = new TextDecoder('utf-8').decode(chunksAll);\n\n// Selesai\nlet commits = JSON.parse(result);\nalert(commits[0].author.login);\n```\n\nMari kita jelaskan langkah demi langkah:\n\n1. Kita menggunakan `fetch` seperti biasa, tetapi sebagai ganti penggunaan `response.json()`, kita menggunakan _stream reader_ `response.body.getReader()`.\n   Perlu dicatat, kita tidak dapat menggunakan kedua metode ini untuk membaca respon yang sama: baik menggunakan _reader_ atau metode respon untuk mendapatkan hasilnya.\n\n2. Sebelum proses pembacaan, kita bisa mendapatkan keseluruhan panjang respon dari `Content-Length` _header_.\n\n   Itu kemungkinan tidak ada untuk permintaan lintas sumber (lihat bagian <info:fetch-crossorigin>) dan secara teknis _server_ tidak harus menyetelnya. Tetapi biasanya ada.\n\n3. Penggil `await reader.read()` sampai selesai.\n\n   Kita mengumpulkan potongan respon di larik `chunks`. Itu penting karena setelah respon diterima, kita tidak akan dapat \"membaca ulang\" menggunakan `response.json()` atau dengan cara lain (Anda dapat mencobanya dan akan ada galat)\n\n4. Pada akhirnya, kita memiliki `chunks` - sebuah larik dari potongan byte ʻUint8Array`. Kita perlu menggabungkannya menjadi satu data tunggal. Sayangnya, tidak ada metode tunggal yang dapat menggabungkannya, jadi ada beberapa kode untuk melakukannya:\n   1. Kita membuat `chunksAll = new Uint8Array (receivedLength)` - larik dengan tipe yang sama dengan panjang gabungan.\n   2. Kemudian gunakan metode `.set (chunk, position)` untuk menyalin setiap `chunk` satu demi satu di dalamnya.\n5. Kita mendapatkan hasilnya di `chunksAll`. Ini adalah larik byte, bukan string.\n\n   Untuk membuat string, kita perlu menafsirkan byte ini. [TextDecoder](info:text-decoder) bawaan dapat melakukan hal itu. Lalu kita bisa `JSON.parse`, jika diperlukan.\n\n   Bagaimana jika kita membutuhkan konten biner, bukan string? Itu bahkan lebih sederhana. Ganti langkah 4 dan 5 dengan satu baris yang membuat `Blob` dari semua potongan:\n\n   ```js\n   let blob = new Blob(chunks);\n   ```\n\nPada akhirnya kita memiliki hasil (sebagai string atau blob, apa pun yang Anda inginkan), dan pelacakan kemajuan dalam prosesnya.\n\nSekali lagi, harap diperhatikan, itu bukan untuk kemajuan _upload_ (sekarang belum ada cara dengan `fetch`), hanya untuk kemajuan _download_.\n\nDan juga, jika ukurannya tidak diketahui, kita harus memeriksa `acceptLength` di loop dan menghentikannya setelah mencapai batas tertentu. Sehingga `chunks` tidak akan melebihkan memori."
  },
  {
    "path": "5-network/03-fetch-progress/progress.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/03-fetch-progress/progress.view/long.txt",
    "content": "A long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\nA long file. Длинный файл. 长文件.\n"
  },
  {
    "path": "5-network/04-fetch-abort/article.md",
    "content": "# Fetch: Membatalkan\n\nSeperti yang kita tahu, `fetch` mengembalikan `promise` dan Javascript secara umum tidak memiliki konsep untuk \"membatalkan\" sebuah janji (`Promise`). Jadi, bagaimana kita bisa membatalkan `fetch` yang sedang dijalankan? misalnya, jika tindakan pengguna pada situs kita mengindikasikan bahwa `fetch` sudah tidak dibutuhkan lagi.\n\nTerdapat objek bawaan khusus untuk tujuan ini: `AbortContoller`. Metode tersebut dapat digunakan untuk membatalkan tidak hanya `fetch`, tetapi tugas asingkron lainnya.\n\nPenggunaannya sangat mudah:\n\n## Objek AbortController\n\nMembuat sebuah pengontrol (_controller_)\n\n```js\nlet controller = new AbortController();\n```\n\nPengontrol adalah objek yang sangat sederhana.\n\n- Hanya memiliki satu metode `abort()`,\n- dan sebuah properti `signal` yang memungkinkan untuk menyetel `listener` pada objek pengontrol.\n\nKetika `abort()` dipanggil:\n\n- `contoller.signal` mengeluarkan _event_ `\"abort\"`\n- Properti `controller.signal.aborted` menjadi bernilai `true`\n\nSecara Umum, kita memiliki dua pihak dalam prosesnya:\n\n1. Satu pihak yang melaksanakan tindakan terntentu ketika operasi dibatalkan, itu menyetel _listener_ pada `controller.signal`.\n2. Satu pihak lainnya yang membatalkan: itu memanggil `controller.abort()` ketika diperlukan.\n\nBerikut contoh lengkapnya (tanpa `fetch`)\n\n```js run\nlet controller = new AbortController();\nlet signal = controller.signal;\n\n// Pihak yang melakukan tindakan tertentu ketika operasi dibatalkan\n// mendapatkan objek \"signal\"\n// dan menyetel 'listener' untuk memicu ketika controller.abort() dipanggil\nsignal.addEventListener('abort', () => alert('abort!'));\n\n// Pihak lain yang membatalkan (penjelasnnya nanti)\ncontroller.abort(); // gagalkan!\n\n// Pemicu 'event' dan nilai 'signal.aborted' menjadi 'true'\nalert(signal.aborted); // true\n```\n\nSeperti yang kita lihat, `AbortController` hanyalah sarana untuk meneruskan _event_ `abort()` ketika dipanggil.\n\nKita dapat mengimplementasikan jenis _event listener_ yang sama pada kode kita, walaupun tanpa objek `AbortController` sama sekali.\n\nTetapi yang berharga adalah `fetch` tahu bagaimana bekerja dengan objek `AborController`, itu terintegrasi dengannya.\n\n## Menggunakan dengan fetch\n\nUntuk bisa membatalkan `fetch`, teruskan properti `signal` dari `AbortController` sebagai opsi `fetch`:\n\n```js\nlet controller = new AbortController();\nfetch(url, {\n  signal: controller.signal,\n});\n```\n\nMetode `fetch` mengetahui bagaimana cara bekerja dengan `AbortController`. Itu akan mendengarkan (_listen_) _event_ `abort` pada properti `signal`.\n\nSekarang, untuk membatalkannya, panggil `controller.abort()`:\n\n```js\ncontroller.abort();\n```\n\nKita sudah selesai: `fetch` mendapatkan _event_ dari properti `signal` dan membatalkan _request_.\n\nKetika `fetch` dibatalkan, maka `promise` akan ditolak dengan galat `AboutError`, jadi kita dapat menanganinya. Misalnya dengan `try ... catch`.\n\nBerikut adalah contoh penuh untuk menggagalkan `fetch` setelah 1 detik:\n\n```js run async\n// gagalkan setelah satu detik\nlet controller = new AbortController();\nsetTimeout(() => controller.abort(), 1000);\n\ntry {\n  let response = await fetch('/article/fetch-abort/demo/hang', {\n    signal: controller.signal,\n  });\n} catch (err) {\n  if (err.name == 'AbortError') {\n    // menangani ketika digagalkan\n    alert('Aborted!');\n  } else {\n    throw err;\n  }\n}\n```\n\n## AbortController dapat ditingkatkan (_scalable_)\n\n`AbortContoller` bersifat _scalable_, itu memungkinkan untuk membatalkan beberapa `fetch` sekaligus.\n\nBerikut adalah sketsa dari kode yang terdapat proses `fetch` ke banyak `urls` secara paralel, dan menggunakan pengontrol tunggal untuk membatalkan semua proses tersebut:\n\n```js\nlet urls = [...]; // daftar dari url yang akan diambil secara paralel\n\nlet controller = new AbortController();\n\n// larik dari 'fetch promise'\nlet fetchJobs = urls.map(url => fetch(url, {\n  signal: controller.signal\n}));\n\nlet results = await Promise.all(fetchJobs);\n\n// jika controller.fetch() dipanggil dari tempat tertentu\n// itu akan menggagalkan semua 'fetch'\n```\n\nJika kita memiliki tugas asingkron kita sendiri yang berbeda dari `fetch`, kita dapat menggunakan `AbortController` tunggal untuk menghentikannya, bersama dengan metode `fetch`.\n\nKita hanya perlu untuk `listen` pada _event_ `abort` di tugas kita;\n\n```js\nlet urls = [...];\nlet controller = new AbortController();\n\nlet ourJob = new Promise((resolve, reject) => { // tugas kita\n  ...\n  controller.signal.addEventListener('abort', reject);\n});\n\nlet fetchJobs = urls.map(url => fetch(url, { // pemanggilan metode fetch\n  signal: controller.signal\n}));\n\n// Menunggu semua metode fetch dan tugas kita secara paralel\nlet results = await Promise.all([...fetchJobs, ourJob]);\n\n// Jika controller.abort() dipanggil dari suatu tempat,\n// itu menggagalkan semua metode fetch dan tugas kita\n```\n\n## Kesimpulan\n\n- `AbortController` adalah sebuah objek sederhana yang menghasilkan _event_ `abort` pada properti `signal` ketika metode `abort()` dipanggil (dan juga menyetel `signal.aborted` menjadi `true`).\n- `fetch` terintegrasi dengannya: kita meneruskan properti `signal` sebagai opsi, dan kemudian `fetch` mendengarkan event yang dihasilkan `AbortController`. Jadi itu menjadi mungkin ketika ingin menggagalkan proses `fetch`.\n- Kita dapat menggunakan `AbortConntroller` pada kode kita. Interaksi \"pemanggilan `abort()`\" -> \"Mendengarkan (_listen_) _event_ `abort`\" sederhana dan universal. Kita dapat menggunakannya walaupun tanpa `fetch`.\n"
  },
  {
    "path": "5-network/04-fetch-abort/demo.view/server.js",
    "content": "const Koa = require('koa');\nconst app = new Koa();\n\nconst Router = require('koa-router');\n\nlet router = new Router();\n\nrouter.get('/hang', async (ctx) => {\n  await new Promise(() => {});\n});\n\napp\n  .use(router.routes())\n  .use(router.allowedMethods());\n\n\nif (!module.parent) {\n  http.createServer(app.callback()).listen(8080);\n} else {\n  exports.accept = app.callback();\n}\n"
  },
  {
    "path": "5-network/05-fetch-crossorigin/1-do-we-need-origin/solution.md",
    "content": "Kita membutuhkan *`Origin`*, karena terkadang *`Referer`* tidak digunakan. Misalnya, saat kita *`fetch`* halaman HTTP dari HTTPS(mengakses yang kurang aman dari yang aman), maka tidak memakai *`Referer`*.\n\n[Kebijakan keamanan konten](http://en.wikipedia.org/wiki/Content_Security_Policy) mungkin akan melarang untuk pengiriman *`Referer`*.\n\nSeperti yang kita lihat, *`fetch`* memiliki opsi untuk mencegah mengirim `Referer`, dan mengizinkan untuk mengubahnya ( di situs yang sama).\n\nDari Spesifikasi, *`Referer`* adalah header HTTP opsional.\n\nTepatnya karena *`Referer`* tidak dapat diandalkan, dibuat lah `Origin`. Browser menjamin *`Origin` *yang benar untuk *request* *cross-origin*."
  },
  {
    "path": "5-network/05-fetch-crossorigin/1-do-we-need-origin/task.md",
    "content": "importance: 5\n\n---\n\n# Kenapa kita membutuhkan Origin?\n\nSeperti yang kamu ketahui, Ada pembaca *header* HTTP *`Referer`*, yang biasanya berisi *url* dari halaman yang dimulai dari *request* jaringan.\n\nMisalnya, saat pengambilan `http://google.com` dari `http://javacript.info/some/url`, *header* terlihat seperti ini:\n\n```\nAccept: */*\nAccept-Charset: utf-8\nAccept-Encoding: gzip,deflate,sdch\nConnection: keep-alive\nHost: google.com\n*!*\nOrigin: http://javascript.info\nReferer: http://javascript.info/some/url\n*/!*\n```\n\nSeperti yang kamu lihat, baik *`Referer`* dan *`Origin`* ada.\n\nPertanyaan:\n\n1. Kenapa *`Origin`* dibutuhkan, jika *`Referer`* memiliki lebih banyak informasi?\n2. Apakah mungkin jika *`fetch`* tidak memakai *`Referer`* atau *`Origin`*, atau apakah *`fetch`* itu salah?\n"
  },
  {
    "path": "5-network/05-fetch-crossorigin/article.md",
    "content": "# Fetch: *request Cross-Origin*\n\nJika kita mengirim *request* *`fetch`* dari situs web lain, itu mungkin akan gagal.\n\nMisalnya, mari coba *fetch* dari `http://example.com`:\n\n```js run async\ntry {\n  await fetch('http://example.com');\n} catch(err) {\n  alert(err); // Failed to fetch (Gagal untuk *fetch*)\n}\n```\n\n*Fetch* akan gagal, seperti yang diperkirakan.\n\nKonsep dasarnya dari *origin* -- tiga serangkai *domain*/port/protokol.\n\n*request cross-origin* --  yang dikirimkan ke *domain* lain (atau sob*domain*) atau protokol atau *port* -- membutuhkan *header* khusus dari sisi *remote*.\n\nKebijakan yang disebut *\"CORS\"*: *Cross-Origin Resource Sharing*.\n\n## Kenapa *CORS* dibutuhkan? sejarah singkat\n\n*CORS* ada untuk melindungi internet dari *hacker* jahat.\n\nSeriously. Let's make a very brief historical digression.\nSerius, Mari kita membuat sejarah yang singkat. \n\n**Untuk bertahun-tahun skrip dari satu situs tidak bisa mengakses konten dari situs lainnya**\n\nAturan aman yang kuat tersebut adalah dasar dari keamanan internet. Contoh skrip jahat dari situs web `hacker.com` tidak bisa mengakses pesan dari pengguna di situs web `gmail.com`. Orang-orang merasa aman.\n\nJavascript juga tidak memiliki metode khusus untuk menampilkan *request* jaringan pada saat itu. Ini adalah bahasa mainan untuk menghias halaman web.\n\nTetapi pengembang web menuntut lebih banyak kuasa. Bebagai trik dibuat untuk mengatasi batasan dan membuat *request* kepada situs web lainnya.\n\n### Menggunakan Form\n\nSatu cara untuk berkomunikasi dengan *server* lain adalah dengan mengirimkan `<form>`. Orang-orang mengirimkannya pada `<iframe>`, hanya untuk tetap di halaman saat ini, seperti ini:\n\n```html\n<!-- form target -->\n*!*\n<iframe name=\"iframe\"></iframe>\n*/!*\n\n<!-- form bisa dihasilkan secara dinamis dan dikirikan oleh Javascript -->\n*!*\n<form target=\"iframe\" method=\"POST\" action=\"http://another.com/…\">\n*/!*\n  ...\n</form>\n```\n\nJadi, ada kemungkinan untuk membuat *request* GET/POST dari situs lain, meski tanpa metode jaringan, karena form bisa mengirimkan data dari mana saja. Tetapi dilarang untuk mengakses konten `<iframe>` dari situs lainnya, tidak ada kemungkinan untuk membaca responnya.\n\nTepatnya, sebenarnya ada trik untuk melakukannya, mereka membutuhkan skrip khusus dari *iframe* dan halaman. Jadi komunikasi dengan *iframe* secara teknis mungkin. Saat ini tidak ada gunanya untuk membahas lebih detail, biarkan dinosaurus ini istirahat dengan tenang.  \n\n### Menggunakan Skrip\n\nTrik lainnya adalah menggunakan *tag* `script`. Skrip bisa memiliki `src`, dengan **domain** apapun, contoh `<script src=\"http://another.com/…\">`. Ada kemungkinan untuk menjalankan skrip dari situs web apa saja. \n\nJika sebuah situs web, contoh `another.com` berniat untuk membuka data untuk akses seperti ini, kemudian protokol yang disebut \"JSONP (JSON with padding)\" akan digunakan.\n\nBegini cara kerjanya.\n\nMari kita katakan jika, di sisi kita, perlu untuk mengambil data dari `http://another.com`, seperti cuaca:\n\n1. Pertama, terlebih dahulu, kita mendeklarasikan fungsi global untuk menerima data, contoh `gotWeather`.\n\n    ```js\n    // 1. Deklarasikan fungsi untuk memproses data cuaca\n    function gotWeather({ temperature, humidity }) {\n      alert(`temperature: ${temperature}, humidity: ${humidity}`);\n    }\n    ```\n2. Selanjutnya kita membuat *tag* `<script>` dengan `src=\"http://another.com/weather.json?callback=gotWeather\"`, menggunakan nama fungsi sebagai parameter *URL* *`callback`*.\n\n    ```js\n    let script = document.createElement('script');\n    script.src = `http://another.com/weather.json?callback=gotWeather`;\n    document.*body*.append(script);\n    ```\n3. *server* *remote* `another.com` menghasilkan skrip dinamis yang memanggil `gotWeather(...)` dengan data yang ingin kita terima.\n\n    ```js\n    // Jawaban dari *server* yang diharapkan seperti ini:\n    gotWeather({\n      temperature: 25,\n      humidity: 78\n    });\n    ```\n4. When the remote script loads and executes, `gotWeather` runs, and, as it's our function, we have the data.\n4. Jika skrip *remote* memuat dan dijalankan, `gotWeather` dijalankan di fungsi kita, kita memiliki data.\n\nItu berhasil, dan tidak melanggar keamanan, karena kedua sisi setuju untuk meluluskan data dengan cara ini. Dan jika kedua sisi setuju, ini bukanlah peretasan. Masih ada servis yang menyediakan akses seperti ini, karena cara ini bisa berfungsi bahkan untuk browser lama. \n\nKemudian, metode jaringan muncul di browser Javascript.\n\nMulanya, *request cross-orogin* itu dilarang. Tetapi dari hasil diskusi yang panjang, *request cross-origin* diizinkan, tetapi dengan kemampuan baru yang memerlukan izin tegas dari *server*, diekpresikan di *header* khusus.\n\n## Request yang aman\n\nAda dua tipe dari *request cross-origin*:\n\n1. Safe requests / Permintaan Aman.\n2. All the others.\n\nPermintaan Aman lebih mudah dibuat, jadi mari kita mulai dengannya.\n\nPermintaan aman jika memenuhi dua kondisi:\n\n1. [Safe method](https://fetch.spec.whatwg.org/#cors-safelisted-method): GET, POST or HEAD\n2. [Safe headers](https://fetch.spec.whatwg.org/#cors-safelisted-request-header) -- satu-satunya header khusus yang diizinkan adalah:\n    - `Accept`,\n    - `Accept-Language`,\n    - `Content-Language`,\n    - `Content-Type` dengan nilai `application/x-www-form-urlencoded`, `multipart/form-data` atau `text/plain`.\n\n*request* lainnya akan dipertimbangkan \"tidak aman\". Misalnya, *request* dengan metode `PUT` atau dengan `API-key` *header* HTTP tidak sesuai batasannya.\n\n**Perbedaan yang mendasar adalah \"*request* yang aman\" bisa dibuat menggunakan `<form>` atau `<script>`, tanpa menggunakan metode khusus apa saja.**\n\nJadi, meski *server* sudah lama tetap bisa menerima *request* yang aman.\n\nBertentangan dengan itu, *request* dengan *header* tidak standar. contoh metode `DELETE` tidak bisa dibuat seperti ini. Jadi *server* lama mungkin berasumsi bahwa *request* ini berasal dari sumber istimewa, \"karena situs web tidak bisa mengirimkan *request*\".\n\nSaat kita mencoba untuk membuat *request* tidak aman, browser mengirimkan *request* khusus *\"preflight\"* yang menanyakan *server* -- apakah bisa untuk menerima *request cross-origin* atau tidak?\n\nDan, kecuali *server* secara eksplisit mengonfirmasi hal itu dengan *header*, maka *request* tidak aman tidak akan dikirimkan.\n\nSekarang kita akan membahasnya lebih lanjut.\n\n## *CORS* untuk *request* yang aman\n\nJika *request* adalah *request cross-origin*, maka browser akan selalu menambahkan *header* `Origin` di dalam *request*. \n\n\nMisalnya, jika kita *request* `https://anywhere.com/*request*` dari `https://javascript.info/page`, *header* akan seperti ini:\n\n```http\nGET /*request*\nHost: anywhere.com\n*!*\nOrigin: https://javascript.info\n*/!*\n...\n```\n\nSeperti yang kamu lihat, *header* `Origin` berisi persis seperti asal (*domain*/protocol/port), tanpa *path*.\n\n*Server* bisa memeriksa `Origin` dan jika setuju untuk menerima **request** tersebut, tambahkan *header* khusus `Access-Control-Allow-Origin` di responnya. *header* tersebut harus berisi asal yang diizinkan (di kasus kita `https://javascript.info`), atau bintang `*` . Maka responnya sukses, atau mungkin saja error.\n\nBrowser memainkan peran sebagai penengah terpercaya disini:\n1. Browser memastikan `Origin` yang benar dikirimkan dengan *request* cross-origin.\n2. Browser memeriksa untuk perizinan di responnya, jika ada maka Javascript akan diizinkan untuk mengakses respon, jika tidak maka akan gagal dengan error.\n\n![](xhr-another-*domain*.svg)\n\nDisini ada contoh dari respon *server* permisif:\n```http\n200 OK\nContent-Type:text/html; charset=UTF-8\n*!*\nAccess-Control-Allow-Origin: https://javascript.info\n*/!*\n```\n\n## *Header* respon\n\nUntuk **request** cross-origin, secara default Javascript hanya dapat mengakses file yang disebut *header* respon aman:\n\n- `*cache*-Control`\n- `Content-Language`\n- `Content-Type`\n- `Expires`\n- `Last-Modified`\n- `Pragma`\n\nMengakses *header* respon lainnya akan menghasilkan error.\n\n```smart\nTidak ada *header* `Content-Length` di daftar!\n\n*Header* ini berisi panjang respon lengkap. Jadi, jika mengunduh sesuatu dan ingin melacak persentase progres, maka diperlukan izin tambahan untuk mengakses *header* tersebut (lihat di bawah).\n```\n\nUntuk memberikan Javascript akses ke *header* respon lainnya, *server* harus mengirimkan *header*  `Access-Control-Expose-*header*s`. Ini berisi daftar yang dipisahkan dengan koma dari nama *header* tidak aman yang seharusnya dibuat untuk bisa di akses. \n\nContoh:\n\n```http\n200 OK\nContent-Type:text/html; charset=UTF-8\nContent-Length: 12345\nAPI-Key: 2c9de507f2c54aa1\nAccess-Control-Allow-Origin: https://javascript.info\n*!*\nAccess-Control-Expose-*header*s: Content-Length,API-Key\n*/!*\n```\n\nDengan *header* `Access-Control-Expose-headers`, skrip diizinkan untuk membaca *header* `Content-Length` dan `API-Key` dari respon. \n\n## *Request* tidak aman\n\nKita bisa menggunakan metode HTTP apa saja: bukan hanya `GET/POST`, tetapi juga `PATCH`, `DELETE` dan lainnya.\n\nBeberapa waktu yang lalu tidak ada orang yang membayangkan bahwa halaman web bisa membuat *request* seperti ini. Jadi masih ada servis web yang memperlakukan metode tidak standar sebagai sinyal: \"Itu bukan browser\". Mereka dapat memperhitungkannya saat memeriksa hak akses.\n\nJadi untuk menghindari kesalah pahaman, *request* tidak aman -- yang tidak bisa diselesaikan di waktu yang lama, browser tidak akan membuat *request* tersebut secara langsung. Sebelum dikirimkan ke pendahuluan yang disebut *request* \"preflight\", meminta izin.\n\n*Request* preflight menggunakan metode `OPTIONS`, tidak ada *body* dan dua *header*:\n\n- *Header* `Access-Control-request-Method` memiliki metode *request* tidak aman.\n- *Header* `Access-Control-request-headers` menyediakan daftar yang dipisahkan dengan koma dari *header* HTTP tidak aman.\n\nJika *server* setuju untuk melayani *request*, maka akan merespon dengan *body* kosong, status 200 dan *header*:\n\n-`Access-Control-Allow-Origin` harus dari `*` atau dari *request* asal, misalnya `https://javascript.info`, untuk mengizinkannya.\n- `Access-Control-Allow-Methods` harus memiliki metode yang diizinkan.\n- `Access-Control-Allow-*header*s` harus berisi daftar *header* yang diizinkan.\n- Sebagai tambahan, *header* `Access-Control-Max-Age` dapat menentukan jumlah detik untuk menyimpan izin ke *cache*. Jadi browser tidak perlu mengirim preflight untuk  *request* selanjutnya yang memenuhi izin yang diberikan.\n\n![](xhr-preflight.svg)\n\nMari kita lihat bagaimana cara kerjanya langkah demi langkah di sebuah contoh, untuk *request* cross-origin `PATCH` (metode ini sering digunakan untuk memperbarui data):\n\n```js\nlet response = await fetch('https://site.com/service.json', {\n  method: 'PATCH',\n  *header*s: {\n    'Content-Type': 'application/json',\n    'API-Key': 'secret'\n  }\n});\n```\n\nAda tiga alasan kenapa *request* bisa menjadi tidak aman( cukup pilih satu) :\n- Metode `PATCH`.\n- `Content-Type` bukan salah satu dari: *header* `application/x-www-form-*URL*encoded`, `multipart/form-data`, `text/plain`.\n- `API-key` \"tidak aman\".\n\n### Langkah 1 (*request preflight*)\n\nSebelum mengirim permintaan tersebut, browser, dengan sendirinya akan mengirimkan request preflight seperti ini:\n\n```http\nOPTIONS /service.json\nHost: site.com\nOrigin: https://javascript.info\nAccess-Control-*request*-Method: PATCH\nAccess-Control-*request*-*header*s: Content-Type,API-Key\n```\n\n- Metode: `OPTIONS`.\n- Path -- Sama persis dengan *request* utama: `/service.json`.\n- *header* khusus *cross-origin*:\n    - `Origin` -- asal sumber.\n    - `Access-Control-*request*-Method` -- metode *request*.\n    - `Access-Control-*request*-*header*s` -- daftar yang dipisahkan dengan koma dari *header* tidak aman.\n\n### Langkah 2 (respon *preflight*)\n*Server* harus merespon dengan status 200 dan *header*:\n- `Access-Control-Allow-Origin: https://javascript.info`\n- `Access-Control-Allow-Methods: PATCH`\n- `Access-Control-Allow-*header*s: Content-Type,API-Key`.\n\nItu memungkinkan komunikasi di masa depan, jika tidak, maka akan di picu error.\n\nJika *server* mengharapkan metode dan *header* lain di masa depan, akan masuk akal jika mengizinkan terlebih dahulu dengan menambahkannya di daftar.\n\nMisalnya, respon ini mengizinkan *header* `PUT`, `DELETE` dan tambahan.\n\n```http\n200 OK\nAccess-Control-Allow-Origin: https://javascript.info\nAccess-Control-Allow-Methods: PUT,PATCH,DELETE\nAccess-Control-Allow-*header*s: API-Key,Content-Type,If-Modified-Since,*cache*-Control\nAccess-Control-Max-Age: 86400\n```\n\nSekarang browser bisa melihat `PATCH` di dalam `Access-Control-Allow-Methods` dan `Content-Type,API-Key` di dalam daftar `Access-Control-Allow-headers`, sehingga akan mengirimkan permintaan utama.\n\nJika ada *header* `Access-Control-Max-Age` dengan jumlah detik, maka izin *preflight* akan di simpan dalam *cache* untuk waktu tertentu. Respon diatas akan di simpan di *cache* selama 86400 detik (satu hari). Dengan jangka waktu ini, permintaan selanjutnya tidak akan menyebabkan *preflight*. Dengan asumsi bahwa mereka sesuai dengan *cache* yang diizinkan, mereka akan dikirim langsung.\n\n### Langkah 3 (*request* sebenarnya)\n\nSaat *preflight* berhasil, browser akan membuat *request* utama. Algoritmanya sama dengan *request* yang aman.\n\n*request* yang utama memiliki *header* `Origin` (karena *request* ini memang *cross-origin*)\n\n```http\nPATCH /service.json\nHost: site.com\nContent-Type: application/json\nAPI-Key: secret\nOrigin: https://javascript.info\n```\n\n### Langkah 4 (Respon sebenarnya)\n\n*Server* tidak boleh lupa untuk menambahkan `Access-Control-Allow-Origin` pada respon utamanya. *preflight* yang berhasil tidak membebaskan dari itu:\n\n```http\nAccess-Control-Allow-Origin: https://javascript.info\n```\n\nMaka Javascript bisa membaca respon dari *server* utama. \n\n```smart\n*Request preflight* terjadi \"dibelakang layar\", hal ini tidak terlihat oleh Javascript.\n\nJavascript hanya mendapatkan respon dari *request* utama atau error jika tidak mendapatkan izin dari *server*.\n```\n\n## Kredensial\n\n*Request cross-origin* yang dimulai oleh kode Javascript secara default, tidak membawa kredensial apapun ( *cookie* atau autentikasi HTTP).\n\nItu tidak biasa untuk *request* HTTP. Biasanya, *request* pada `htttp://site.com` didampingi oleh semua *cookie* dari *domain* tersebut. Tetapi *request cross-origin* yang dibuat dengan metode Javascript memiliki pengecualian. \n\nContoh, `fetch('http://another.com')` tidak mengirim *cookie* apapun bahkan yang (!) memiliki *domain* `another.com`. \n\nKenapa?\n\nKarena *request* dengan kredensial lebih kuat daripada tanpa kredensial. Jika diizinkan, kredensial bisa memberikan Javascript kekuatan penuh untuk bertindak atas nama pengguna dan mengakses informasi sensitif menggunakan kredensial mereka.\n\nApakah *server* benar-benar mempercayai skrip itu? maka harus secara eksplisit mengizinkan *request* dengan kredensial dengan *header* tambahan.\n\nUntuk mengirim kredensial di `fetch`, kita perlu menambahkan opsi `credentials: \"include\"`, seperti ini:\n\n```js\nfetch('http://another.com', {\n  credentials: \"include\"\n});\n```\n\nSekarang `fetch` mengirimkan *cookie* yang berasal dari `another.com` dengan *request* dari situs tersebut.\n\nJika *server* setuju untuk menerima *request dengan credentials*, harus ditambahkan *header* `Access-Control-Allow-Credentials: true` pada responnya, sebagai tambahan dari `Access-Control-Allow-Origin`.\n\nContoh:\n\n```http\n200 OK\nAccess-Control-Allow-Origin: https://javascript.info\nAccess-Control-Allow-Credentials: true\n```\n\nPerlu dicatat: `Access-Control-Allow-Origin` dilarang untuk menggunakan bintang `*` untuk *request* dengan kredensial. Seperti yang ditunjukkan diatas, harus menyediakan asal yang tepat. Itu adalah salah satu ukuran keamanan tambahan, untuk memastikan bahwa *server* benar-benar mengetahui siapa yang dipercayai untuk membuat *request* tersebut.\n\n## Ringkasan\n\nDari sudut pandang browser, ada dua jenis *request cross-origin*: aman dan lain lainnya.\n\n[*Request* yang aman](http://www.w3.org/TR/cors/#terminology) harus memenuhi kondisi dibawah:\n- Metode: GET, POST atau HEAD.\n- *header* -- bisa kita atur jika:\n    - `Accept`\n    - `Accept-Language`\n    - `Content-Language`\n    - `Content-Type` to the value `application/x-www-form-*URL*encoded`, `multipart/form-data` atau `text/plain`.\n\nPerbedaan mendasar dari *request* aman bisa dilakukan sejak zaman kuno menggunakan *tag* `<form>` atau `<script>`, sedangkan yang tidak aman tidak mungkin dijalankan oleh browser untuk waktu yang lama.\n\nJadi, Perbedaan praktis dari *request* yang aman di kirimkan segera, dengan *header* `Origin`, sedangkan untuk yang lainnya browser membuat awalan *request \"preflight\"* , untuk meminta izin.\n\n**Untuk *request* yang aman**\n\n- → Browser mengirimkan *header* `Origin` dengan asalnya.\n- ← Untuk *request* tanpa kredensial (tidak terkirim secara default), *server* harus diatur: \n    - `Access-Control-Allow-Origin` menjadi `*` atau nilai yang sama dengan `Origin`\n- ← Untuk *request* dengan kredensial, *server* harus diatur:\n    - `Access-Control-Allow-Origin` menjadi nilai yang sama dengan `Origin`\n    - `Access-Control-Allow-Credentials` menjadi `true`\n\nSebagai tambahan, untuk memberikan Javascript akses kepada *header* respon apapun kecuali `*cache*-Control`,  `Content-Language`, `Content-Type`, `Expires`, `Last-Modified` or `Pragma`, *server* harus mendaftarkan *header* yang diizinkan di *header* `Access-Control-Expose-headers`.\n\n**Untuk *request* tidak aman, awalan *request \"preflight\"* diberikan sebelum *request* yang diminta:**\n\n- → Browser mengirimkan *request* `OPTIONS` di *URL* yang sama, dengan *header*:\n    - `Access-Control-request-Method` memiliki metode *request*.\n    - `Access-Control-request-headers` mendaftarkan *header request* yang tidak aman.\n- ← *Server* harus merespon dengan status 200 dan *header*:\n    - `Access-Control-Allow-Methods` dengan daftar metode yang diizinkan,\n     - `Access-Control-Allow-headers` dengan daftar *header* yang diizinkan,\n     - `Access-Control-Max-Age` dengan jumlah detik untuk izin menyimpan di *cache*.\n\n- Lalu *request* yang sebenarnya di kirimkan, skema yang \"aman\" sebelumnya akan diterapkan.\n"
  },
  {
    "path": "5-network/06-fetch-api/article.md",
    "content": "\n# Fetch API\n\nSo far, we know quite a bit about `fetch`.\n\nLet's see the rest of API, to cover all its abilities.\n\n```smart\nPlease note: most of these options are used rarely. You may skip this chapter and still use `fetch` well.\n\nStill, it's good to know what `fetch` can do, so if the need arises, you can return and read the details.\n```\n\nHere's the full list of all possible `fetch` options with their default values (alternatives in comments):\n\n```js\nlet promise = fetch(url, {\n  method: \"GET\", // POST, PUT, DELETE, etc.\n  headers: {\n    // the content type header value is usually auto-set\n    // depending on the request body\n    \"Content-Type\": \"text/plain;charset=UTF-8\"\n  },\n  body: undefined // string, FormData, Blob, BufferSource, or URLSearchParams\n  referrer: \"about:client\", // or \"\" to send no Referer header,\n  // or an url from the current origin\n  referrerPolicy: \"no-referrer-when-downgrade\", // no-referrer, origin, same-origin...\n  mode: \"cors\", // same-origin, no-cors\n  credentials: \"same-origin\", // omit, include\n  cache: \"default\", // no-store, reload, no-cache, force-cache, or only-if-cached\n  redirect: \"follow\", // manual, error\n  integrity: \"\", // a hash, like \"sha256-abcdef1234567890\"\n  keepalive: false, // true\n  signal: undefined, // AbortController to abort request\n  window: window // null\n});\n```\n\nAn impressive list, right?\n\nWe fully covered `method`, `headers` and `body` in the chapter <info:fetch>.\n\nThe `signal` option is covered in <info:fetch-abort>.\n\nNow let's explore the remaining capabilities.\n\n## referrer, referrerPolicy\n\nThese options govern how `fetch` sets the HTTP `Referer` header.\n\nUsually that header is set automatically and contains the url of the page that made the request. In most scenarios, it's not important at all, sometimes, for security purposes, it makes sense to remove or shorten it.\n\n**The `referrer` option allows to set any `Referer` (within the current origin) or remove it.**\n\nTo send no referer, set an empty string:\n```js\nfetch('/page', {\n*!*\n  referrer: \"\" // no Referer header\n*/!*\n});\n```\n\nTo set another url within the current origin:\n\n```js\nfetch('/page', {\n  // assuming we're on https://javascript.info\n  // we can set any Referer header, but only within the current origin\n*!*\n  referrer: \"https://javascript.info/anotherpage\"\n*/!*\n});\n```\n\n**The `referrerPolicy` option sets general rules for `Referer`.**\n\nRequests are split into 3 types:\n\n1. Request to the same origin.\n2. Request to another origin.\n3. Request from HTTPS to HTTP (from safe to unsafe protocol).\n\nUnlike the `referrer` option that allows to set the exact `Referer` value, `referrerPolicy` tells the browser general rules for each request type.\n\nPossible values are described in the [Referrer Policy specification](https://w3c.github.io/webappsec-referrer-policy/):\n\n- **`\"no-referrer-when-downgrade\"`** -- the default value: full `Referer` is always sent, unless we send a request from HTTPS to HTTP (to the less secure protocol).\n- **`\"no-referrer\"`** -- never send `Referer`.\n- **`\"origin\"`** -- only send the origin in `Referer`, not the full page URL, e.g. only `http://site.com` instead of `http://site.com/path`.\n- **`\"origin-when-cross-origin\"`** -- send the full `Referer` to the same origin, but only the origin part for cross-origin requests (as above).\n- **`\"same-origin\"`** -- send the full `Referer` to the same origin, but no `Referer` for cross-origin requests.\n- **`\"strict-origin\"`** -- send only the origin, not the `Referer` for HTTPS→HTTP requests.\n- **`\"strict-origin-when-cross-origin\"`** -- for same-origin send the full `Referer`, for cross-origin send only the origin, unless it's HTTPS→HTTP request, then send nothing.\n- **`\"unsafe-url\"`** -- always send the full url in `Referer`, even for HTTPS→HTTP requests.\n\nHere's a table with all combinations:\n\n| Value | To same origin | To another origin | HTTPS→HTTP |\n|-------|----------------|-------------------|------------|\n| `\"no-referrer\"` | - | - | - |\n| `\"no-referrer-when-downgrade\"` or `\"\"` (default) | full | full | - |\n| `\"origin\"` | origin | origin | origin |\n| `\"origin-when-cross-origin\"` | full | origin | origin |\n| `\"same-origin\"` | full | - | - |\n| `\"strict-origin\"` | origin | origin | - |\n| `\"strict-origin-when-cross-origin\"` | full | origin | - |\n| `\"unsafe-url\"` | full | full | full |\n\nLet's say we have an admin zone with a URL structure that shouldn't be known from outside of the site.\n\nIf we send a `fetch`, then by default it always sends the `Referer` header with the full url of our page (except when we request from HTTPS to HTTP, then no `Referer`).\n\nE.g. `Referer: https://javascript.info/admin/secret/paths`.\n\nIf we'd like other websites know only the origin part, not the URL-path, we can set the option:\n\n```js\nfetch('https://another.com/page', {\n  // ...\n  referrerPolicy: \"origin-when-cross-origin\" // Referer: https://javascript.info\n});\n```\n\nWe can put it to all `fetch` calls, maybe integrate into JavaScript library of our project that does all requests and uses `fetch` inside.\n\nIts only difference compared to the default behavior is that for requests to another origin `fetch` sends only the origin part of the URL (e.g. `https://javascript.info`, without path). For requests to our origin we still get the full `Referer` (maybe useful for debugging purposes).\n\n```smart header=\"Referrer policy is not only for `fetch`\"\nReferrer policy, described in the [specification](https://w3c.github.io/webappsec-referrer-policy/), is not just for `fetch`, but more global.\n\nIn particular, it's possible to set the default policy for the whole page using the `Referrer-Policy` HTTP header, or per-link, with `<a rel=\"noreferrer\">`.\n```\n\n## mode\n\nThe `mode` option is a safe-guard that prevents occasional cross-origin requests:\n\n- **`\"cors\"`** -- the default, cross-origin requests are allowed, as described in <info:fetch-crossorigin>,\n- **`\"same-origin\"`** -- cross-origin requests are forbidden,\n- **`\"no-cors\"`** -- only safe cross-origin requests are allowed.\n\nThis option may be useful when the URL for `fetch` comes from a 3rd-party, and we want a \"power off switch\" to limit cross-origin capabilities.\n\n## credentials\n\nThe `credentials` option specifies whether `fetch` should send cookies and HTTP-Authorization headers with the request.\n\n- **`\"same-origin\"`** -- the default, don't send for cross-origin requests,\n- **`\"include\"`** -- always send, requires `Accept-Control-Allow-Credentials` from cross-origin server in order for JavaScript to access the response, that was covered in the chapter <info:fetch-crossorigin>,\n- **`\"omit\"`** -- never send, even for same-origin requests.\n\n## cache\n\nBy default, `fetch` requests make use of standard HTTP-caching. That is, it respects the `Expires` and `Cache-Control` headers, sends `If-Modified-Since` and so on. Just like regular HTTP-requests do.\n\nThe `cache` options allows to ignore HTTP-cache or fine-tune its usage:\n\n- **`\"default\"`** -- `fetch` uses standard HTTP-cache rules and headers,\n- **`\"no-store\"`** -- totally ignore HTTP-cache, this mode becomes the default if we set a header `If-Modified-Since`, `If-None-Match`, `If-Unmodified-Since`, `If-Match`, or `If-Range`,\n- **`\"reload\"`** -- don't take the result from HTTP-cache (if any), but populate the cache with the response (if the response headers permit this action),\n- **`\"no-cache\"`** -- create a conditional request if there is a cached response, and a normal request otherwise. Populate HTTP-cache with the response,\n- **`\"force-cache\"`** -- use a response from HTTP-cache, even if it's stale. If there's no response in HTTP-cache, make a regular HTTP-request, behave normally,\n- **`\"only-if-cached\"`** -- use a response from HTTP-cache, even if it's stale. If there's no response in HTTP-cache, then error. Only works when `mode` is `\"same-origin\"`.\n\n## redirect\n\nNormally, `fetch` transparently follows HTTP-redirects, like 301, 302 etc.\n\nThe `redirect` option allows to change that:\n\n- **`\"follow\"`** -- the default, follow HTTP-redirects,\n- **`\"error\"`** -- error in case of HTTP-redirect,\n- **`\"manual\"`** -- allows to process HTTP-redirects manually. In case of redirect, we'll get a special response object, with `response.type=\"opaqueredirect\"` and zeroed/empty status and most other properies.\n\n## integrity\n\nThe `integrity` option allows to check if the response matches the known-ahead checksum.\n\nAs described in the [specification](https://w3c.github.io/webappsec-subresource-integrity/), supported hash-functions are SHA-256, SHA-384, and SHA-512, there might be others depending on the browser.\n\nFor example, we're downloading a file, and we know that it's SHA-256 checksum is \"abcdef\" (a real checksum is longer, of course).\n\nWe can put it in the `integrity` option, like this:\n\n```js\nfetch('http://site.com/file', {\n  integrity: 'sha256-abcdef'\n});\n```\n\nThen `fetch` will calculate SHA-256 on its own and compare it with our string. In case of a mismatch, an error is triggered.\n\n## keepalive\n\nThe `keepalive` option indicates that the request may \"outlive\" the webpage that initiated it.\n\nFor example, we gather statistics on how the current visitor uses our page (mouse clicks, page fragments he views), to analyze and improve the user experience.\n\nWhen the visitor leaves our page -- we'd like to save the data to our server.\n\nWe can use the `window.onunload` event for that:\n\n```js run\nwindow.onunload = function() {\n  fetch('/analytics', {\n    method: 'POST',\n    body: \"statistics\",\n*!*\n    keepalive: true\n*/!*\n  });\n};\n```\n\nNormally, when a document is unloaded, all associated network requests are aborted. But the `keepalive` option tells the browser to perform the request in the background, even after it leaves the page. So this option is essential for our request to succeed.\n\nIt has a few limitations:\n\n- We can't send megabytes: the body limit for `keepalive` requests is 64KB.\n    - If we need to gather a lot of statistics about the visit, we should send it out regularly in packets, so that there won't be a lot left for the last `onunload` request.\n    - This limit applies to all `keepalive` requests together. In other words, we can perform multiple `keepalive` requests in parallel, but the sum of their body lengths should not exceed 64KB.\n- We can't handle the server response if the document is unloaded. So in our example `fetch` will succeed due to `keepalive`, but subsequent functions won't work.\n    - In most cases, such as sending out statistics, it's not a problem, as the server just accepts the data and usually sends an empty response to such requests.\n"
  },
  {
    "path": "5-network/06-fetch-api/post.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/06-fetch-api/post.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.method == 'POST') {\n    let chunks = [];\n    let length = 0;\n\n    req.on('data', function (data) {\n      chunks.push(data);\n      length += data.length;\n\n      // Too much POST data, kill the connection!\n      if (length > 1e6) {\n        request.connection.destroy();\n      }\n    });\n\n    req.on('end', function() {\n      // let post = JSON.parse(chunks.join(''));\n\n      if (req.url == '/user') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: 'User saved' }));\n      } else if (req.url == '/image') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Image saved\", imageSize: length }));\n      } else {\n        res.writeHead(404);\n        res.end(\"Not found\");\n      }\n    });\n\n\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/07-url/article.md",
    "content": "\n# URL objects\n\nThe built-in [URL](https://url.spec.whatwg.org/#api) class provides a convenient interface for creating and parsing URLs.\n\nThere are no networking methods that require exactly a `URL` object, strings are good enough. So technically we don't have to use `URL`. But sometimes it can be really helpful.\n\n## Creating a URL\n\nThe syntax to create a new `URL` object:\n\n```js\nnew URL(url, [base])\n```\n\n- **`url`** -- the full URL or only path (if base is set, see below),\n- **`base`** -- an optional base URL: if set and `url` argument has only path, then the URL is generated relative to `base`.\n\nFor example:\n\n```js\nlet url = new URL('https://javascript.info/profile/admin');\n```\n\nThese two URLs are same:\n\n```js run\nlet url1 = new URL('https://javascript.info/profile/admin');\nlet url2 = new URL('/profile/admin', 'https://javascript.info');\n\nalert(url1); // https://javascript.info/profile/admin\nalert(url2); // https://javascript.info/profile/admin\n```\n\nWe can easily create a new URL based on the path relative to an existing URL:\n\n```js run\nlet url = new URL('https://javascript.info/profile/admin');\nlet newUrl = new URL('tester', url);\n\nalert(newUrl); // https://javascript.info/profile/tester\n```\n\nThe `URL` object immediately allows us to access its components, so it's a nice way to parse the url, e.g.:\n\n```js run\nlet url = new URL('https://javascript.info/url');\n\nalert(url.protocol); // https:\nalert(url.host);     // javascript.info\nalert(url.pathname); // /url\n```\n\nHere's the cheatsheet for URL components:\n\n![](url-object.svg)\n\n- `href` is the full url, same as `url.toString()`\n- `protocol` ends with the colon character `:`\n- `search` - a string of parameters, starts with the question mark `?`\n- `hash` starts with the hash character `#`\n- there may be also `user` and `password` properties if HTTP authentication is present: `http://login:password@site.com` (not painted above, rarely used).\n\n\n```smart header=\"We can pass `URL` objects to networking (and most other) methods instead of a string\"\nWe can use a `URL` object in `fetch` or `XMLHttpRequest`, almost everywhere where a URL-string is expected.\n\nGenerally, the `URL` object can be passed to any method instead of a string, as most methods will perform the string conversion, that turns a `URL` object into a string with full URL.\n```\n\n## SearchParams \"?...\"\n\nLet's say we want to create a url with given search params, for instance, `https://google.com/search?query=JavaScript`.\n\nWe can provide them in the URL string:\n\n```js\nnew URL('https://google.com/search?query=JavaScript')\n```\n\n...But parameters need to be encoded if they contain spaces, non-latin letters, etc (more about that below).\n\nSo there's a URL property for that: `url.searchParams`, an object of type [URLSearchParams](https://url.spec.whatwg.org/#urlsearchparams).\n\nIt provides convenient methods for search parameters:\n\n- **`append(name, value)`** -- add the parameter by `name`,\n- **`delete(name)`** -- remove the parameter by `name`,\n- **`get(name)`** -- get the parameter by `name`,\n- **`getAll(name)`** -- get all parameters with the same `name` (that's possible, e.g. `?user=John&user=Pete`),\n- **`has(name)`** -- check for the existence of the parameter by `name`,\n- **`set(name, value)`** -- set/replace the parameter,\n- **`sort()`** -- sort parameters by name, rarely needed,\n- ...and it's also iterable, similar to `Map`.\n\nAn example with parameters that contain spaces and punctuation marks:\n\n```js run\nlet url = new URL('https://google.com/search');\n\nurl.searchParams.set('q', 'test me!'); // added parameter with a space and !\n\nalert(url); // https://google.com/search?q=test+me%21\n\nurl.searchParams.set('tbs', 'qdr:y'); // added parameter with a colon :\n\n// parameters are automatically encoded\nalert(url); // https://google.com/search?q=test+me%21&tbs=qdr%3Ay\n\n// iterate over search parameters (decoded)\nfor(let [name, value] of url.searchParams) {\n  alert(`${name}=${value}`); // q=test me!, then tbs=qdr:y\n}\n```\n\n\n## Encoding\n\nThere's a standard [RFC3986](https://tools.ietf.org/html/rfc3986) that defines which characters are allowed in URLs and which are not.\n\nThose that are not allowed, must be encoded, for instance non-latin letters and spaces - replaced with their UTF-8 codes, prefixed by `%`, such as `%20` (a space can be encoded by `+`, for historical reasons, but that's an exception).\n\nThe good news is that `URL` objects handle all that automatically. We just supply all parameters unencoded, and then convert the `URL` to string:\n\n```js run\n// using some cyrillic characters for this example\n\nlet url = new URL('https://ru.wikipedia.org/wiki/Тест');\n\nurl.searchParams.set('key', 'ъ');\nalert(url); //https://ru.wikipedia.org/wiki/%D0%A2%D0%B5%D1%81%D1%82?key=%D1%8A\n```\n\nAs you can see, both `Тест` in the url path and `ъ` in the parameter are encoded.\n\nThe URL became longer, because each cyrillic letter is represented with two bytes in UTF-8, so there are two `%..` entities.\n\n### Encoding strings\n\nIn old times, before `URL` objects appeared, people used strings for URLs.\n\nAs of now, `URL` objects are often more convenient, but strings can still be used as well. In many cases using a string makes the code shorter.\n\nIf we use a string though, we need to encode/decode special characters manually.\n\nThere are built-in functions for that:\n\n- [encodeURI](mdn:/JavaScript/Reference/Global_Objects/encodeURI) - encodes URL as a whole.\n- [decodeURI](mdn:/JavaScript/Reference/Global_Objects/decodeURI) - decodes it back.\n- [encodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/encodeURIComponent) - encodes a URL component, such as a search parameter, or a hash, or a pathname.\n- [decodeURIComponent](mdn:/JavaScript/Reference/Global_Objects/decodeURIComponent) - decodes it back.\n\nA natural question is: \"What's the difference between `encodeURIComponent` and `encodeURI`? When we should use either?\"\n\nThat's easy to understand if we look at the URL, that's split into components in the picture above:\n\n```\nhttps://site.com:8080/path/page?p1=v1&p2=v2#hash\n```\n\nAs we can see, characters such as `:`, `?`, `=`, `&`, `#` are allowed in URL.\n\n...On the other hand, if we look at a single URL component, such as a search parameter, these characters must be encoded, not to break the formatting.\n\n- `encodeURI` encodes only characters that are totally forbidden in URL.\n- `encodeURIComponent` encodes same characters, and, in addition to them, characters `#`, `$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?` and `@`.\n\nSo, for a whole URL we can use `encodeURI`:\n\n```js run\n// using cyrillic characters in url path\nlet url = encodeURI('http://site.com/привет');\n\nalert(url); // http://site.com/%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82\n```\n\n...While for URL parameters we should use `encodeURIComponent` instead:\n\n```js run\nlet music = encodeURIComponent('Rock&Roll');\n\nlet url = `https://google.com/search?q=${music}`;\nalert(url); // https://google.com/search?q=Rock%26Roll\n```\n\nCompare it with `encodeURI`:\n\n```js run\nlet music = encodeURI('Rock&Roll');\n\nlet url = `https://google.com/search?q=${music}`;\nalert(url); // https://google.com/search?q=Rock&Roll\n```\n\nAs we can see, `encodeURI` does not encode `&`, as this is a legit character in URL as a whole.\n\nBut we should encode `&` inside a search parameter, otherwise, we get `q=Rock&Roll` - that is actually `q=Rock` plus some obscure parameter `Roll`. Not as intended.\n\nSo we should use only `encodeURIComponent` for each search parameter, to correctly insert it in the URL string. The safest is to encode both name and value, unless we're absolutely sure that it has only allowed characters.\n\n````smart header=\"Encoding difference compared to `URL`\"\nClasses [URL](https://url.spec.whatwg.org/#url-class) and [URLSearchParams](https://url.spec.whatwg.org/#interface-urlsearchparams) are based on the latest URI specification: [RFC3986](https://tools.ietf.org/html/rfc3986), while `encode*` functions are based on the obsolete version [RFC2396](https://www.ietf.org/rfc/rfc2396.txt).\n\nThere are a few differences, e.g. IPv6 addresses are encoded differently:\n\n```js run\n// valid url with IPv6 address\nlet url = 'http://[2607:f8b0:4005:802::1007]/';\n\nalert(encodeURI(url)); // http://%5B2607:f8b0:4005:802::1007%5D/\nalert(new URL(url)); // http://[2607:f8b0:4005:802::1007]/\n```\n\nAs we can see, `encodeURI` replaced square brackets `[...]`, that's not correct, the reason is: IPv6 urls did not exist at the time of RFC2396 (August 1998).\n\nSuch cases are rare, `encode*` functions work well most of the time.\n````\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/article.md",
    "content": "# XMLHttpRequest\n\n`XMLHttpRequest` is a built-in browser object that allows to make HTTP requests in JavaScript.\n\nDespite of having the word \"XML\" in its name, it can operate on any data, not only in XML format. We can upload/download files, track progress and much more.\n\nRight now, there's another, more modern method `fetch`, that somewhat deprecates `XMLHttpRequest`.\n\nIn modern web-development `XMLHttpRequest` is used for three reasons:\n\n1. Historical reasons: we need to support existing scripts with `XMLHttpRequest`.\n2. We need to support old browsers, and don't want polyfills (e.g. to keep scripts tiny).\n3. We need something that `fetch` can't do yet, e.g. to track upload progress.\n\nDoes that sound familiar? If yes, then all right, go on with `XMLHttpRequest`. Otherwise, please head on to <info:fetch>.\n\n## The basics\n\nXMLHttpRequest has two modes of operation: synchronous and asynchronous.\n\nLet's see the asynchronous first, as it's used in the majority of cases.\n\nTo do the request, we need 3 steps:\n\n1. Create `XMLHttpRequest`:\n    ```js\n    let xhr = new XMLHttpRequest();\n    ```\n    The constructor has no arguments.\n\n2. Initialize it, usually right after `new XMLHttpRequest`:\n    ```js\n    xhr.open(method, URL, [async, user, password])\n    ```\n\n    This method specifies the main parameters of the request:\n\n    - `method` -- HTTP-method. Usually `\"GET\"` or `\"POST\"`.\n    - `URL` -- the URL to request, a string, can be [URL](info:url) object.\n    - `async` -- if explicitly set to `false`, then the request is synchronous, we'll cover that a bit later.\n    - `user`, `password` -- login and password for basic HTTP auth (if required).\n\n    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`.\n\n3. Send it out.\n\n    ```js\n    xhr.send([body])\n    ```\n\n    This method opens the connection and sends the request to server. The optional `body` parameter contains the request body.\n\n    Some request methods like `GET` do not have a body. And some of them like `POST` use `body` to send the data to the server. We'll see examples of that later.\n\n4. Listen to `xhr` events for response.\n\n    These three events are the most widely used:\n    - `load` -- when the request is complete (even if HTTP status is like 400 or 500), and the response is fully downloaded.\n    - `error` -- when the request couldn't be made, e.g. network down or invalid URL.\n    - `progress` -- triggers periodically while the response is being downloaded, reports how much has been downloaded.\n\n    ```js\n    xhr.onload = function() {\n      alert(`Loaded: ${xhr.status} ${xhr.response}`);\n    };\n\n    xhr.onerror = function() { // only triggers if the request couldn't be made at all\n      alert(`Network Error`);\n    };\n\n    xhr.onprogress = function(event) { // triggers periodically\n      // event.loaded - how many bytes downloaded\n      // event.lengthComputable = true if the server sent Content-Length header\n      // event.total - total number of bytes (if lengthComputable)\n      alert(`Received ${event.loaded} of ${event.total}`);\n    };\n    ```\n\nHere's a full example. The code below loads the URL at `/article/xmlhttprequest/example/load` from the server and prints the progress:\n\n```js run\n// 1. Create a new XMLHttpRequest object\nlet xhr = new XMLHttpRequest();\n\n// 2. Configure it: GET-request for the URL /article/.../load\nxhr.open('GET', '/article/xmlhttprequest/example/load');\n\n// 3. Send the request over the network\nxhr.send();\n\n// 4. This will be called after the response is received\nxhr.onload = function() {\n  if (xhr.status != 200) { // analyze HTTP status of the response\n    alert(`Error ${xhr.status}: ${xhr.statusText}`); // e.g. 404: Not Found\n  } else { // show the result\n    alert(`Done, got ${xhr.response.length} bytes`); // response is the server response\n  }\n};\n\nxhr.onprogress = function(event) {\n  if (event.lengthComputable) {\n    alert(`Received ${event.loaded} of ${event.total} bytes`);\n  } else {\n    alert(`Received ${event.loaded} bytes`); // no Content-Length\n  }\n\n};\n\nxhr.onerror = function() {\n  alert(\"Request failed\");\n};\n```\n\nOnce the server has responded, we can receive the result in the following `xhr` properties:\n\n`status`\n: HTTP status code (a number): `200`, `404`, `403` and so on, can be `0` in case of a non-HTTP failure.\n\n`statusText`\n: HTTP status message (a string): usually `OK` for `200`, `Not Found` for `404`, `Forbidden` for `403` and so on.\n\n`response` (old scripts may use `responseText`)\n: The server response body.\n\nWe can also specify a timeout using the corresponding property:\n\n```js\nxhr.timeout = 10000; // timeout in ms, 10 seconds\n```\n\nIf the request does not succeed within the given time, it gets canceled and `timeout` event triggers.\n\n````smart header=\"URL search parameters\"\nTo add parameters to URL, like `?name=value`, and ensure the proper encoding, we can use [URL](info:url) object:\n\n```js\nlet url = new URL('https://google.com/search');\nurl.searchParams.set('q', 'test me!');\n\n// the parameter 'q' is encoded\nxhr.open('GET', url); // https://google.com/search?q=test+me%21\n```\n\n````\n\n## Response Type\n\nWe can use `xhr.responseType` property to set the response format:\n\n- `\"\"` (default) -- get as string,\n- `\"text\"` -- get as string,\n- `\"arraybuffer\"` -- get as `ArrayBuffer` (for binary data, see chapter <info:arraybuffer-binary-arrays>),\n- `\"blob\"` -- get as `Blob` (for binary data, see chapter <info:blob>),\n- `\"document\"` -- get as XML document (can use XPath and other XML methods) or HTML document (based on the MIME type of the received data),\n- `\"json\"` -- get as JSON (parsed automatically).\n\nFor example, let's get the response as JSON:\n\n```js run\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/article/xmlhttprequest/example/json');\n\n*!*\nxhr.responseType = 'json';\n*/!*\n\nxhr.send();\n\n// the response is {\"message\": \"Hello, world!\"}\nxhr.onload = function() {\n  let responseObj = xhr.response;\n  alert(responseObj.message); // Hello, world!\n};\n```\n\n```smart\nIn the old scripts you may also find `xhr.responseText` and even `xhr.responseXML` properties.\n\nThey exist for historical reasons, to get either a string or XML document. Nowadays, we should set the format in `xhr.responseType` and get `xhr.response` as demonstrated above.\n```\n\n## Ready states\n\n`XMLHttpRequest` changes between states as it progresses. The current state is accessible as  `xhr.readyState`.\n\nAll states, as in [the specification](https://xhr.spec.whatwg.org/#states):\n\n```js\nUNSENT = 0; // initial state\nOPENED = 1; // open called\nHEADERS_RECEIVED = 2; // response headers received\nLOADING = 3; // response is loading (a data packet is received)\nDONE = 4; // request complete\n```\n\nAn `XMLHttpRequest` object travels them in the order `0` -> `1` -> `2` -> `3` -> ... -> `3` -> `4`. State `3` repeats every time a data packet is received over the network.\n\nWe can track them using `readystatechange` event:\n\n```js\nxhr.onreadystatechange = function() {\n  if (xhr.readyState == 3) {\n    // loading\n  }\n  if (xhr.readyState == 4) {\n    // request finished\n  }\n};\n```\n\nYou can find `readystatechange` listeners in really old code, it's there for historical reasons, as there was a time when there were no `load` and other events. Nowadays, `load/error/progress` handlers deprecate it.\n\n## Aborting request\n\nWe can terminate the request at any time. The call to `xhr.abort()` does that:\n\n```js\nxhr.abort(); // terminate the request\n```\n\nThat triggers `abort` event, and `xhr.status` becomes `0`.\n\n## Synchronous requests\n\nIf in the `open` method the third parameter `async` is set to `false`, the request is made synchronously.\n\nIn other words, JavaScript execution pauses at `send()` and resumes when the response is received. Somewhat like `alert` or `prompt` commands.\n\nHere's the rewritten example, the 3rd parameter of `open` is `false`:\n\n```js\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/article/xmlhttprequest/hello.txt', *!*false*/!*);\n\ntry {\n  xhr.send();\n  if (xhr.status != 200) {\n    alert(`Error ${xhr.status}: ${xhr.statusText}`);\n  } else {\n    alert(xhr.response);\n  }\n} catch(err) { // instead of onerror\n  alert(\"Request failed\");\n}\n```\n\nIt might look good, but synchronous calls are used rarely, because they block in-page JavaScript till the loading is complete. In some browsers it becomes impossible to scroll. If a synchronous call takes too much time, the browser may suggest to close the \"hanging\" webpage.\n\nMany advanced capabilities of `XMLHttpRequest`, like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. Also, as you can see, no progress indication.\n\nBecause of all that, synchronous requests are used very sparingly, almost never. We won't talk about them any more.\n\n## HTTP-headers\n\n`XMLHttpRequest` allows both to send custom headers and read headers from the response.\n\nThere are 3 methods for HTTP-headers:\n\n`setRequestHeader(name, value)`\n: Sets the request header with the given `name` and `value`.\n\n    For instance:\n\n    ```js\n    xhr.setRequestHeader('Content-Type', 'application/json');\n    ```\n\n    ```warn header=\"Headers limitations\"\n    Several headers are managed exclusively by the browser, e.g. `Referer` and `Host`.\n    The full list is [in the specification](https://xhr.spec.whatwg.org/#the-setrequestheader()-method).\n\n    `XMLHttpRequest` is not allowed to change them, for the sake of user safety and correctness of the request.\n    ```\n\n    ````warn header=\"Can't remove a header\"\n    Another peculiarity of `XMLHttpRequest` is that one can't undo `setRequestHeader`.\n\n    Once the header is set, it's set. Additional calls add information to the header, don't overwrite it.\n\n    For instance:\n\n    ```js\n    xhr.setRequestHeader('X-Auth', '123');\n    xhr.setRequestHeader('X-Auth', '456');\n\n    // the header will be:\n    // X-Auth: 123, 456\n    ```\n    ````\n\n`getResponseHeader(name)`\n: Gets the response header with the given `name` (except `Set-Cookie` and `Set-Cookie2`).\n\n    For instance:\n\n    ```js\n    xhr.getResponseHeader('Content-Type')\n    ```\n\n`getAllResponseHeaders()`\n: Returns all response headers, except `Set-Cookie` and `Set-Cookie2`.\n\n    Headers are returned as a single line, e.g.:\n\n    ```http\n    Cache-Control: max-age=31536000\n    Content-Length: 4260\n    Content-Type: image/png\n    Date: Sat, 08 Sep 2012 16:53:16 GMT\n    ```\n\n    The line break between headers is always `\"\\r\\n\"` (doesn't depend on OS), so we can easily split it into individual headers. The separator between the name and the value is always a colon followed by a space `\": \"`. That's fixed in the specification.\n\n    So, if we want to get an object with name/value pairs, we need to throw in a bit JS.\n\n    Like this (assuming that if two headers have the same name, then the latter one overwrites the former one):\n\n    ```js\n    let headers = xhr\n      .getAllResponseHeaders()\n      .split('\\r\\n')\n      .reduce((result, current) => {\n        let [name, value] = current.split(': ');\n        result[name] = value;\n        return result;\n      }, {});\n\n    // headers['Content-Type'] = 'image/png'\n    ```\n\n## POST, FormData\n\nTo make a POST request, we can use the built-in [FormData](mdn:api/FormData) object.\n\nThe syntax:\n\n```js\nlet formData = new FormData([form]); // creates an object, optionally fill from <form>\nformData.append(name, value); // appends a field\n```\n\nWe create it, optionally fill from a form, `append` more fields if needed, and then:\n\n1. `xhr.open('POST', ...)` – use `POST` method.\n2. `xhr.send(formData)` to submit the form to the server.\n\nFor instance:\n\n```html run refresh\n<form name=\"person\">\n  <input name=\"name\" value=\"John\">\n  <input name=\"surname\" value=\"Smith\">\n</form>\n\n<script>\n  // pre-fill FormData from the form\n  let formData = new FormData(document.forms.person);\n\n  // add one more field\n  formData.append(\"middle\", \"Lee\");\n\n  // send it out\n  let xhr = new XMLHttpRequest();\n  xhr.open(\"POST\", \"/article/xmlhttprequest/post/user\");\n  xhr.send(formData);\n\n  xhr.onload = () => alert(xhr.response);\n</script>\n```\n\nThe form is sent with `multipart/form-data` encoding.\n\nOr, if we like JSON more, then `JSON.stringify` and send as a string.\n\nJust don't forget to set the header `Content-Type: application/json`, many server-side frameworks automatically decode JSON with it:\n\n```js\nlet xhr = new XMLHttpRequest();\n\nlet json = JSON.stringify({\n  name: \"John\",\n  surname: \"Smith\"\n});\n\nxhr.open(\"POST\", '/submit')\nxhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');\n\nxhr.send(json);\n```\n\nThe `.send(body)` method is pretty omnivore. It can send almost any `body`, including `Blob` and `BufferSource` objects.\n\n## Upload progress\n\nThe `progress` event triggers only on the downloading stage.\n\nThat is: if we `POST` something, `XMLHttpRequest` first uploads our data (the request body), then downloads the response.\n\nIf we're uploading something big, then we're surely more interested in tracking the upload progress. But `xhr.onprogress` doesn't help here.\n\nThere's another object, without methods, exclusively to track upload events: `xhr.upload`.\n\nIt generates events, similar to `xhr`, but `xhr.upload` triggers them solely on uploading:\n\n- `loadstart` -- upload started.\n- `progress` -- triggers periodically during the upload.\n- `abort` -- upload aborted.\n- `error` -- non-HTTP error.\n- `load` -- upload finished successfully.\n- `timeout` -- upload timed out (if `timeout` property is set).\n- `loadend` -- upload finished with either success or error.\n\nExample of handlers:\n\n```js\nxhr.upload.onprogress = function(event) {\n  alert(`Uploaded ${event.loaded} of ${event.total} bytes`);\n};\n\nxhr.upload.onload = function() {\n  alert(`Upload finished successfully.`);\n};\n\nxhr.upload.onerror = function() {\n  alert(`Error during the upload: ${xhr.status}`);\n};\n```\n\nHere's a real-life example: file upload with progress indication:\n\n```html run\n<input type=\"file\" onchange=\"upload(this.files[0])\">\n\n<script>\nfunction upload(file) {\n  let xhr = new XMLHttpRequest();\n\n  // track upload progress\n*!*\n  xhr.upload.onprogress = function(event) {\n    console.log(`Uploaded ${event.loaded} of ${event.total}`);\n  };\n*/!*\n\n  // track completion: both successful or not\n  xhr.onloadend = function() {\n    if (xhr.status == 200) {\n      console.log(\"success\");\n    } else {\n      console.log(\"error \" + this.status);\n    }\n  };\n\n  xhr.open(\"POST\", \"/article/xmlhttprequest/post/upload\");\n  xhr.send(file);\n}\n</script>\n```\n\n## Cross-origin requests\n\n`XMLHttpRequest` can make cross-origin requests, using the same CORS policy as [fetch](info:fetch-crossorigin).\n\nJust like `fetch`, it doesn't send cookies and HTTP-authorization to another origin by default. To enable them, set `xhr.withCredentials` to `true`:\n\n```js\nlet xhr = new XMLHttpRequest();\n*!*\nxhr.withCredentials = true;\n*/!*\n\nxhr.open('POST', 'http://anywhere.com/request');\n...\n```\n\nSee the chapter <info:fetch-crossorigin> for details about cross-origin headers.\n\n\n## Summary\n\nTypical code of the GET-request with `XMLHttpRequest`:\n\n```js\nlet xhr = new XMLHttpRequest();\n\nxhr.open('GET', '/my/url');\n\nxhr.send();\n\nxhr.onload = function() {\n  if (xhr.status != 200) { // HTTP error?\n    // handle error\n    alert( 'Error: ' + xhr.status);\n    return;\n  }\n\n  // get the response from xhr.response\n};\n\nxhr.onprogress = function(event) {\n  // report progress\n  alert(`Loaded ${event.loaded} of ${event.total}`);\n};\n\nxhr.onerror = function() {\n  // handle non-HTTP error (e.g. network down)\n};\n```\n\nThere are actually more events, the [modern specification](https://xhr.spec.whatwg.org/#events) lists them (in the lifecycle order):\n\n- `loadstart` -- the request has started.\n- `progress` -- a data packet of the response has arrived, the whole response body at the moment is in `response`.\n- `abort` -- the request was canceled by the call `xhr.abort()`.\n- `error` -- connection error has occurred, e.g. wrong domain name. Doesn't happen for HTTP-errors like 404.\n- `load` -- the request has finished successfully.\n- `timeout` -- the request was canceled due to timeout (only happens if it was set).\n- `loadend` -- triggers after `load`, `error`, `timeout` or `abort`.\n\nThe `error`, `abort`, `timeout`, and `load` events are mutually exclusive. Only one of them may happen.\n\nThe most used events are load completion (`load`), load failure (`error`), or we can use a single `loadend` handler and check the properties of the request object `xhr` to see what happened.\n\nWe've already seen another event: `readystatechange`. Historically, it appeared long ago, before the specification settled. Nowadays, there's no need to use it, we can replace it with newer events, but it can often be found in older scripts.\n\nIf we need to track uploading specifically, then we should listen to same events on `xhr.upload` object.\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/example.view/index.html",
    "content": "<!DOCTYPE HTML>\n<script>\nfunction run() {\n\n  let xhr = new XMLHttpRequest();\n  write(`readyState=${xhr.readyState}`);\n\n  xhr.open('GET', 'digits');\n  write(`readyState=${xhr.readyState}`);\n\n  xhr.onreadystatechange = function() {\n    write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);\n  };\n\n  xhr.onprogress = function() {\n    write(`readyState=${xhr.readyState}, responseText.length=${xhr.responseText.length}`);\n  };\n\n  xhr.send();\n}\n\nfunction write(text) {\n  let li = log.appendChild(document.createElement('li'));\n  li.innerHTML = text;\n}\n</script>\n\n<button onclick=\"run()\">Load digits</button>\n\n<ul id=\"log\"></ul>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/example.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.');\n\nfunction accept(req, res) {\n\n  if (req.url == '/load') {\n\n    res.writeHead(200, {\n      'Content-Type': 'text/plain',\n      'Cache-Control': 'no-cache',\n      'Content-Length': 90000\n    });\n\n    let i = 0;\n\n    let timer = setInterval(write, 1000);\n    write();\n\n    function write() {\n      res.write(String(i).repeat(10000));\n      i++;\n      if (i == 9) {\n        clearInterval(timer);\n        res.end();\n      }\n\n    }\n  } else if (req.url == '/json') {\n    res.writeHead(200, {\n      // 'Content-Type': 'application/json;charset=utf-8',\n      'Cache-Control': 'no-cache'\n    });\n\n    res.write(JSON.stringify({message: \"Hello, world!\"}));\n    res.end();\n  } else {\n    file.serve(req, res);\n  }\n}\n\n\n\n// ----- запуск accept как сервера из консоли или как модуля ------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/hello.txt",
    "content": "Hello from the server!\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <button onclick=\"loadPhones()\" id=\"button\">Load phones.json!</button>\n\n  <script>\n    function loadPhones() {\n\n      let xhr = new XMLHttpRequest();\n\n      xhr.open('GET', 'phones.json');\n\n\n      xhr.send();\n\n\n      xhr.onreadystatechange = function() {\n        if (xhr.readyState != 4) return;\n\n        button.innerHTML = 'Complete!';\n\n        if (xhr.status != 200) {\n          // handle error\n          alert(xhr.status + ': ' + xhr.statusText);\n        } else {\n          // show result\n          alert(xhr.responseText);\n        }\n\n      }\n\n      button.innerHTML = 'Loading...';\n      button.disabled = true;\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/phones.json",
    "content": "[\n    {\n        \"age\": 0, \n        \"id\": \"motorola-xoom-with-wi-fi\", \n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\", \n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\", \n        \"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).\"\n    }, \n    {\n        \"age\": 1, \n        \"id\": \"motorola-xoom\", \n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\", \n        \"name\": \"MOTOROLA XOOM\\u2122\", \n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 2, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-atrix-4g\", \n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\", \n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\", \n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    }, \n    {\n        \"age\": 3, \n        \"id\": \"dell-streak-7\", \n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\", \n        \"name\": \"Dell Streak 7\", \n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    }, \n    {\n        \"age\": 4, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-gem\", \n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\", \n        \"name\": \"Samsung Gem\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 5, \n        \"carrier\": \"Dell\", \n        \"id\": \"dell-venue\", \n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\", \n        \"name\": \"Dell Venue\", \n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    }, \n    {\n        \"age\": 6, \n        \"carrier\": \"Best Buy\", \n        \"id\": \"nexus-s\", \n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\", \n        \"name\": \"Nexus S\", \n        \"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.\"\n    }, \n    {\n        \"age\": 7, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"lg-axis\", \n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\", \n        \"name\": \"LG Axis\", \n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    }, \n    {\n        \"age\": 8, \n        \"id\": \"samsung-galaxy-tab\", \n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\", \n        \"name\": \"Samsung Galaxy Tab\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 9, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-showcase-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\", \n        \"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\"\n    }, \n    {\n        \"age\": 10, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-2-global-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 2 Global by Motorola\", \n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    }, \n    {\n        \"age\": 11, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-pro-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 Pro by Motorola\", \n        \"snippet\": \"The next generation of DOES.\"\n    }, \n    {\n        \"age\": 12, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-bravo-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\", \n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"An experience to cheer about.\"\n    }, \n    {\n        \"age\": 13, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"motorola-defy-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\", \n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    }, \n    {\n        \"age\": 14, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"t-mobile-mytouch-4g\", \n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\", \n        \"name\": \"T-Mobile myTouch 4G\", \n        \"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.\"\n    }, \n    {\n        \"age\": 15, \n        \"carrier\": \"US Cellular\", \n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\", \n        \"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\"\n    }, \n    {\n        \"age\": 16, \n        \"carrier\": \"Sprint\", \n        \"id\": \"sanyo-zio\", \n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\", \n        \"name\": \"SANYO ZIO\", \n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    }, \n    {\n        \"age\": 17, \n        \"id\": \"samsung-transform\", \n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\", \n        \"name\": \"Samsung Transform\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 18, \n        \"id\": \"t-mobile-g2\", \n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\", \n        \"name\": \"T-Mobile G2\", \n        \"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.\"\n    }, \n    {\n        \"age\": 19, \n        \"id\": \"motorola-charm-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\", \n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones-async.view/server.js",
    "content": "var http = require('http');\nvar url = require('url');\nvar querystring = require('querystring');\nvar static = require('node-static');\nvar file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.url == '/phones.json') {\n    // искусственная задержка для наглядности\n    setTimeout(function() {\n      file.serve(req, res);\n    }, 2000);\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.json",
    "content": "[\n    {\n        \"age\": 0,\n        \"id\": \"motorola-xoom-with-wi-fi\",\n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\",\n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\",\n        \"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).\"\n    },\n    {\n        \"age\": 1,\n        \"id\": \"motorola-xoom\",\n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\",\n        \"name\": \"MOTOROLA XOOM\\u2122\",\n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    },\n    {\n        \"age\": 2,\n        \"carrier\": \"AT&amp;T\",\n        \"id\": \"motorola-atrix-4g\",\n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\",\n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\",\n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    },\n    {\n        \"age\": 3,\n        \"id\": \"dell-streak-7\",\n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\",\n        \"name\": \"Dell Streak 7\",\n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    },\n    {\n        \"age\": 4,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"samsung-gem\",\n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\",\n        \"name\": \"Samsung Gem\\u2122\",\n        \"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.\"\n    },\n    {\n        \"age\": 5,\n        \"carrier\": \"Dell\",\n        \"id\": \"dell-venue\",\n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\",\n        \"name\": \"Dell Venue\",\n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    },\n    {\n        \"age\": 6,\n        \"carrier\": \"Best Buy\",\n        \"id\": \"nexus-s\",\n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\",\n        \"name\": \"Nexus S\",\n        \"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.\"\n    },\n    {\n        \"age\": 7,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"lg-axis\",\n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\",\n        \"name\": \"LG Axis\",\n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    },\n    {\n        \"age\": 8,\n        \"id\": \"samsung-galaxy-tab\",\n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\",\n        \"name\": \"Samsung Galaxy Tab\\u2122\",\n        \"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.\"\n    },\n    {\n        \"age\": 9,\n        \"carrier\": \"Cellular South\",\n        \"id\": \"samsung-showcase-a-galaxy-s-phone\",\n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\",\n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\",\n        \"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\"\n    },\n    {\n        \"age\": 10,\n        \"carrier\": \"Verizon\",\n        \"id\": \"droid-2-global-by-motorola\",\n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\",\n        \"name\": \"DROID\\u2122 2 Global by Motorola\",\n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    },\n    {\n        \"age\": 11,\n        \"carrier\": \"Verizon\",\n        \"id\": \"droid-pro-by-motorola\",\n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\",\n        \"name\": \"DROID\\u2122 Pro by Motorola\",\n        \"snippet\": \"The next generation of DOES.\"\n    },\n    {\n        \"age\": 12,\n        \"carrier\": \"AT&amp;T\",\n        \"id\": \"motorola-bravo-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\",\n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"An experience to cheer about.\"\n    },\n    {\n        \"age\": 13,\n        \"carrier\": \"T-Mobile\",\n        \"id\": \"motorola-defy-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\",\n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    },\n    {\n        \"age\": 14,\n        \"carrier\": \"T-Mobile\",\n        \"id\": \"t-mobile-mytouch-4g\",\n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\",\n        \"name\": \"T-Mobile myTouch 4G\",\n        \"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.\"\n    },\n    {\n        \"age\": 15,\n        \"carrier\": \"US Cellular\",\n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\",\n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\",\n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\",\n        \"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\"\n    },\n    {\n        \"age\": 16,\n        \"carrier\": \"Sprint\",\n        \"id\": \"sanyo-zio\",\n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\",\n        \"name\": \"SANYO ZIO\",\n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    },\n    {\n        \"age\": 17,\n        \"id\": \"samsung-transform\",\n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\",\n        \"name\": \"Samsung Transform\\u2122\",\n        \"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.\"\n    },\n    {\n        \"age\": 18,\n        \"id\": \"t-mobile-g2\",\n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\",\n        \"name\": \"T-Mobile G2\",\n        \"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.\"\n    },\n    {\n        \"age\": 19,\n        \"id\": \"motorola-charm-with-motoblur\",\n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\",\n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\",\n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n<head>\n  <meta charset=\"utf-8\">\n</head>\n<body>\n\n  <button onclick=\"loadPhones()\">Load phones.json!</button>\n\n  <script>\n    function loadPhones() {\n      let xhr = new XMLHttpRequest();\n\n      xhr.open('GET', 'phones.json', false);\n      xhr.send();\n\n      if (xhr.status != 200) {\n        // handle error\n        alert('Error ' + xhr.status + ': ' + xhr.statusText);\n      } else {\n        // show result\n        alert(xhr.responseText);\n      }\n    }\n  </script>\n\n</body>\n</html>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/phones.json",
    "content": "[\n    {\n        \"age\": 0, \n        \"id\": \"motorola-xoom-with-wi-fi\", \n        \"imageUrl\": \"img/phones/motorola-xoom-with-wi-fi.0.jpg\", \n        \"name\": \"Motorola XOOM\\u2122 with Wi-Fi\", \n        \"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).\"\n    }, \n    {\n        \"age\": 1, \n        \"id\": \"motorola-xoom\", \n        \"imageUrl\": \"img/phones/motorola-xoom.0.jpg\", \n        \"name\": \"MOTOROLA XOOM\\u2122\", \n        \"snippet\": \"The Next, Next Generation\\n\\nExperience the future with MOTOROLA XOOM, the world's first tablet powered by Android 3.0 (Honeycomb).\"\n    }, \n    {\n        \"age\": 2, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-atrix-4g\", \n        \"imageUrl\": \"img/phones/motorola-atrix-4g.0.jpg\", \n        \"name\": \"MOTOROLA ATRIX\\u2122 4G\", \n        \"snippet\": \"MOTOROLA ATRIX 4G the world's most powerful smartphone.\"\n    }, \n    {\n        \"age\": 3, \n        \"id\": \"dell-streak-7\", \n        \"imageUrl\": \"img/phones/dell-streak-7.0.jpg\", \n        \"name\": \"Dell Streak 7\", \n        \"snippet\": \"Introducing Dell\\u2122 Streak 7. Share photos, videos and movies together. It\\u2019s small enough to carry around, big enough to gather around.\"\n    }, \n    {\n        \"age\": 4, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-gem\", \n        \"imageUrl\": \"img/phones/samsung-gem.0.jpg\", \n        \"name\": \"Samsung Gem\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 5, \n        \"carrier\": \"Dell\", \n        \"id\": \"dell-venue\", \n        \"imageUrl\": \"img/phones/dell-venue.0.jpg\", \n        \"name\": \"Dell Venue\", \n        \"snippet\": \"The Dell Venue; Your Personal Express Lane to Everything\"\n    }, \n    {\n        \"age\": 6, \n        \"carrier\": \"Best Buy\", \n        \"id\": \"nexus-s\", \n        \"imageUrl\": \"img/phones/nexus-s.0.jpg\", \n        \"name\": \"Nexus S\", \n        \"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.\"\n    }, \n    {\n        \"age\": 7, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"lg-axis\", \n        \"imageUrl\": \"img/phones/lg-axis.0.jpg\", \n        \"name\": \"LG Axis\", \n        \"snippet\": \"Android Powered, Google Maps Navigation, 5 Customizable Home Screens\"\n    }, \n    {\n        \"age\": 8, \n        \"id\": \"samsung-galaxy-tab\", \n        \"imageUrl\": \"img/phones/samsung-galaxy-tab.0.jpg\", \n        \"name\": \"Samsung Galaxy Tab\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 9, \n        \"carrier\": \"Cellular South\", \n        \"id\": \"samsung-showcase-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-showcase-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Showcase\\u2122 a Galaxy S\\u2122 phone\", \n        \"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\"\n    }, \n    {\n        \"age\": 10, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-2-global-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-2-global-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 2 Global by Motorola\", \n        \"snippet\": \"The first smartphone with a 1.2 GHz processor and global capabilities.\"\n    }, \n    {\n        \"age\": 11, \n        \"carrier\": \"Verizon\", \n        \"id\": \"droid-pro-by-motorola\", \n        \"imageUrl\": \"img/phones/droid-pro-by-motorola.0.jpg\", \n        \"name\": \"DROID\\u2122 Pro by Motorola\", \n        \"snippet\": \"The next generation of DOES.\"\n    }, \n    {\n        \"age\": 12, \n        \"carrier\": \"AT&amp;T\", \n        \"id\": \"motorola-bravo-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-bravo-with-motoblur.0.jpg\", \n        \"name\": \"MOTOROLA BRAVO\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"An experience to cheer about.\"\n    }, \n    {\n        \"age\": 13, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"motorola-defy-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-defy-with-motoblur.0.jpg\", \n        \"name\": \"Motorola DEFY\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Are you ready for everything life throws your way?\"\n    }, \n    {\n        \"age\": 14, \n        \"carrier\": \"T-Mobile\", \n        \"id\": \"t-mobile-mytouch-4g\", \n        \"imageUrl\": \"img/phones/t-mobile-mytouch-4g.0.jpg\", \n        \"name\": \"T-Mobile myTouch 4G\", \n        \"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.\"\n    }, \n    {\n        \"age\": 15, \n        \"carrier\": \"US Cellular\", \n        \"id\": \"samsung-mesmerize-a-galaxy-s-phone\", \n        \"imageUrl\": \"img/phones/samsung-mesmerize-a-galaxy-s-phone.0.jpg\", \n        \"name\": \"Samsung Mesmerize\\u2122 a Galaxy S\\u2122 phone\", \n        \"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\"\n    }, \n    {\n        \"age\": 16, \n        \"carrier\": \"Sprint\", \n        \"id\": \"sanyo-zio\", \n        \"imageUrl\": \"img/phones/sanyo-zio.0.jpg\", \n        \"name\": \"SANYO ZIO\", \n        \"snippet\": \"The Sanyo Zio by Kyocera is an Android smartphone with a combination of ultra-sleek styling, strong performance and unprecedented value.\"\n    }, \n    {\n        \"age\": 17, \n        \"id\": \"samsung-transform\", \n        \"imageUrl\": \"img/phones/samsung-transform.0.jpg\", \n        \"name\": \"Samsung Transform\\u2122\", \n        \"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.\"\n    }, \n    {\n        \"age\": 18, \n        \"id\": \"t-mobile-g2\", \n        \"imageUrl\": \"img/phones/t-mobile-g2.0.jpg\", \n        \"name\": \"T-Mobile G2\", \n        \"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.\"\n    }, \n    {\n        \"age\": 19, \n        \"id\": \"motorola-charm-with-motoblur\", \n        \"imageUrl\": \"img/phones/motorola-charm-with-motoblur.0.jpg\", \n        \"name\": \"Motorola CHARM\\u2122 with MOTOBLUR\\u2122\", \n        \"snippet\": \"Motorola CHARM fits easily in your pocket or palm.  Includes MOTOBLUR service.\"\n    }\n]\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/phones.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.url == '/phones.json') {\n    // stall a bit to let \"loading\" message show up\n    setTimeout(function() {\n      file.serve(req, res);\n    }, 2000);\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/post.view/index.html",
    "content": "<!doctype html>\n<script>\n(async () {\n\t\n\tconst response = await fetch('long.txt');\n\tconst reader = response.body.getReader();\n\n\tconst contentLength = +response.headers.get('Content-Length');\n\tlet receivedLength = 0;\n\tlet chunks = [];\n\twhile(true) {\n\t\tconst chunk = await reader.read();\n\n\t\tif (chunk.done) {\n\t\t\tconsole.log(\"done!\");\n\t\t\tbreak;\n\t\t}\n\n\t\tchunks.push(chunk.value);\n\t\treceivedLength += chunk.value.length;\n\t\tconsole.log(`${receivedLength}/${contentLength} received`)\n\t}\n\n\n\tlet chunksMerged = new Uint8Array(receivedLength);\n\tlet length = 0;\n\tfor(let chunk of chunks) {\n\t\tchunksMerged.set(chunk, length);\n\t\tlength += chunk.length;\n\t}\n\n\tlet result = new TextDecoder(\"utf-8\").decode(chunksMerged);\n\tconsole.log(result);\n})();\n\n</script>\n"
  },
  {
    "path": "5-network/08-xmlhttprequest/post.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet file = new static.Server('.', {\n  cache: 0\n});\n\n\nfunction accept(req, res) {\n\n  if (req.method == 'POST') {\n    let chunks = [];\n    let length = 0;\n\n    req.on('data', function (data) {\n      chunks.push(data);\n      length += data.length;\n\n      // More than 10mb, kill the connection!\n      if (length > 1e8) {\n        req.connection.destroy();\n      }\n    });\n\n    req.on('end', function() {\n      // let post = JSON.parse(chunks.join(''));\n\n      if (req.url == '/user') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: 'User saved' }));\n      } else if (req.url == '/image') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Image saved\", imageSize: length }));\n      } else if (req.url == '/upload') {\n        res.writeHead(200, { 'Content-Type': 'application/json' });\n        res.end(JSON.stringify({ message: \"Upload complete\", size: length }));\n      } else {\n        res.writeHead(404);\n        res.end(\"Not found\");\n      }\n    });\n\n\n  } else {\n    file.serve(req, res);\n  }\n\n}\n\n\n// ------ запустить сервер -------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/09-resume-upload/article.md",
    "content": "# Resumable file upload\n\nWith `fetch` method it's fairly easy to upload a file.\n\nHow to resume the upload after lost connection? There's no built-in option for that, but we have the pieces to implement it.\n\nResumable uploads should come with upload progress indication, as we expect big files (if we may need to resume). So, as `fetch` doesn't allow to track upload progress, we'll use [XMLHttpRequest](info:xmlhttprequest).\n\n## Not-so-useful progress event\n\nTo resume upload, we need to know how much was uploaded till the connection was lost.\n\nThere's `xhr.upload.onprogress` to track upload progress.\n\nUnfortunately, it won't help us to resume the upload here, as it triggers when the data is *sent*, but was it received by the server? The browser doesn't know.\n\nMaybe it was buffered by a local network proxy, or maybe the remote server process just died and couldn't process them, or it was just lost in the middle and didn't reach the receiver.\n\nThat's why this event is only useful to show a nice progress bar.\n\nTo resume upload, we need to know *exactly* the number of bytes received by the server. And only the server can tell that, so we'll make an additional request.\n\n## Algorithm\n\n1. First, create a file id, to uniquely identify the file we're going to upload:\n    ```js\n    let fileId = file.name + '-' + file.size + '-' + file.lastModified;\n    ```\n    That's needed for resume upload, to tell the server what we're resuming.\n\n    If the name or the size or the last modification date changes, then there'll be another `fileId`.\n\n2. Send a request to the server, asking how many bytes it already has, like this:\n    ```js\n    let response = await fetch('status', {\n      headers: {\n        'X-File-Id': fileId\n      }\n    });\n\n    // The server has that many bytes\n    let startByte = +await response.text();\n    ```\n\n    This assumes that the server tracks file uploads by `X-File-Id` header. Should be implemented at server-side.\n\n    If the file doesn't yet exist at the server, then the server response should be `0`\n\n3. Then, we can use `Blob` method `slice` to send the file from `startByte`:\n    ```js\n    xhr.open(\"POST\", \"upload\", true);\n\n    // File id, so that the server knows which file we upload\n    xhr.setRequestHeader('X-File-Id', fileId);\n\n    // The byte we're resuming from, so the server knows we're resuming\n    xhr.setRequestHeader('X-Start-Byte', startByte);\n\n    xhr.upload.onprogress = (e) => {\n      console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`);\n    };\n\n    // file can be from input.files[0] or another source\n    xhr.send(file.slice(startByte));\n    ```\n\n    Here we send the server both file id as `X-File-Id`, so it knows which file we're uploading, and the starting byte as `X-Start-Byte`, so it knows we're not uploading it initially, but resuming.\n\n    The server should check its records, and if there was an upload of that file, and the current uploaded size is exactly `X-Start-Byte`, then append the data to it.\n\n\nHere's the demo with both client and server code, written on Node.js.\n\nIt works only partially on this site, as Node.js is behind another server named Nginx, that buffers uploads, passing them to Node.js when fully complete.\n\nBut you can download it and run locally for the full demonstration:\n\n[codetabs src=\"upload-resume\" height=200]\n\nAs we can see, modern networking methods are close to file managers in their capabilities -- control over headers, progress indicator, sending file parts, etc.\n\nWe can implement resumable upload and much more.\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/index.html",
    "content": "<!DOCTYPE HTML>\n\n<script src=\"uploader.js\"></script>\n\n<form name=\"upload\" method=\"POST\" enctype=\"multipart/form-data\" action=\"/upload\">\n  <input type=\"file\" name=\"myfile\">\n  <input type=\"submit\" name=\"submit\" value=\"Upload (Resumes automatically)\">\n</form>\n\n<button onclick=\"uploader.stop()\">Stop upload</button>\n\n\n<div id=\"log\">Progress indication</div>\n\n<script>\n  function log(html) {\n    document.getElementById('log').innerHTML = html;\n    console.log(html);\n  }\n\n  function onProgress(loaded, total) {\n    log(\"progress \" + loaded + ' / ' + total);\n  }\n\n  let uploader;\n\n  document.forms.upload.onsubmit = async function(e) {\n    e.preventDefault();\n\n    let file = this.elements.myfile.files[0];\n    if (!file) return;\n\n    uploader = new Uploader({file, onProgress});\n\n    try {\n      let uploaded = await uploader.upload();\n\n      if (uploaded) {\n        log('success');\n      } else {\n        log('stopped');\n      }\n\n    } catch(err) {\n      console.error(err);\n      log('error');\n    }\n  };\n\n</script>\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/server.js",
    "content": "let http = require('http');\nlet static = require('node-static');\nlet fileServer = new static.Server('.');\nlet path = require('path');\nlet fs = require('fs');\nlet debug = require('debug')('example:resume-upload');\n\nlet uploads = Object.create(null);\n\nfunction onUpload(req, res) {\n\n  let fileId = req.headers['x-file-id'];\n  let startByte = +req.headers['x-start-byte'];\n\n  if (!fileId) {\n    res.writeHead(400, \"No file id\");\n    res.end();\n  }\n\n  // we'll files \"nowhere\"\n  let filePath = '/dev/null';\n  // could use a real path instead, e.g.\n  // let filePath = path.join('/tmp', fileId);\n\n  debug(\"onUpload fileId: \", fileId);\n\n  // initialize a new upload\n  if (!uploads[fileId]) uploads[fileId] = {};\n  let upload = uploads[fileId];\n\n  debug(\"bytesReceived:\" + upload.bytesReceived + \" startByte:\" + startByte)\n\n  let fileStream;\n\n  // if startByte is 0 or not set, create a new file, otherwise check the size and append to existing one\n  if (!startByte) {\n    upload.bytesReceived = 0;\n    fileStream = fs.createWriteStream(filePath, {\n      flags: 'w'\n    });\n    debug(\"New file created: \" + filePath);\n  } else {\n    // we can check on-disk file size as well to be sure\n    if (upload.bytesReceived != startByte) {\n      res.writeHead(400, \"Wrong start byte\");\n      res.end(upload.bytesReceived);\n      return;\n    }\n    // append to existing file\n    fileStream = fs.createWriteStream(filePath, {\n      flags: 'a'\n    });\n    debug(\"File reopened: \" + filePath);\n  }\n\n\n  req.on('data', function(data) {\n    debug(\"bytes received\", upload.bytesReceived);\n    upload.bytesReceived += data.length;\n  });\n\n  // send request body to file\n  req.pipe(fileStream);\n\n  // when the request is finished, and all its data is written\n  fileStream.on('close', function() {\n    if (upload.bytesReceived == req.headers['x-file-size']) {\n      debug(\"Upload finished\");\n      delete uploads[fileId];\n\n      // can do something else with the uploaded file here\n\n      res.end(\"Success \" + upload.bytesReceived);\n    } else {\n      // connection lost, we leave the unfinished file around\n      debug(\"File unfinished, stopped at \" + upload.bytesReceived);\n      res.end();\n    }\n  });\n\n  // in case of I/O error - finish the request\n  fileStream.on('error', function(err) {\n    debug(\"fileStream error\");\n    res.writeHead(500, \"File error\");\n    res.end();\n  });\n\n}\n\nfunction onStatus(req, res) {\n  let fileId = req.headers['x-file-id'];\n  let upload = uploads[fileId];\n  debug(\"onStatus fileId:\", fileId, \" upload:\", upload);\n  if (!upload) {\n    res.end(\"0\")\n  } else {\n    res.end(String(upload.bytesReceived));\n  }\n}\n\n\nfunction accept(req, res) {\n  if (req.url == '/status') {\n    onStatus(req, res);\n  } else if (req.url == '/upload' && req.method == 'POST') {\n    onUpload(req, res);\n  } else {\n    fileServer.serve(req, res);\n  }\n\n}\n\n\n\n\n// -----------------------------------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n  console.log('Server listening at port 8080');\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/09-resume-upload/upload-resume.view/uploader.js",
    "content": "class Uploader {\n\n  constructor({file, onProgress}) {\n    this.file = file;\n    this.onProgress = onProgress;\n\n    // create fileId that uniquely identifies the file\n    // we could also add user session identifier (if had one), to make it even more unique\n    this.fileId = file.name + '-' + file.size + '-' + file.lastModified;\n  }\n\n  async getUploadedBytes() {\n    let response = await fetch('status', {\n      headers: {\n        'X-File-Id': this.fileId\n      }\n    });\n\n    if (response.status != 200) {\n      throw new Error(\"Can't get uploaded bytes: \" + response.statusText);\n    }\n\n    let text = await response.text();\n\n    return +text;\n  }\n\n  async upload() {\n    this.startByte = await this.getUploadedBytes();\n\n    let xhr = this.xhr = new XMLHttpRequest();\n    xhr.open(\"POST\", \"upload\", true);\n\n    // send file id, so that the server knows which file to resume\n    xhr.setRequestHeader('X-File-Id', this.fileId);\n    // send the byte we're resuming from, so the server knows we're resuming\n    xhr.setRequestHeader('X-Start-Byte', this.startByte);\n\n    xhr.upload.onprogress = (e) => {\n      this.onProgress(this.startByte + e.loaded, this.startByte + e.total);\n    };\n\n    console.log(\"send the file, starting from\", this.startByte);\n    xhr.send(this.file.slice(this.startByte));\n\n    // return\n    //   true if upload was successful,\n    //   false if aborted\n    // throw in case of an error\n    return await new Promise((resolve, reject) => {\n\n      xhr.onload = xhr.onerror = () => {\n        console.log(\"upload end status:\" + xhr.status + \" text:\" + xhr.statusText);\n\n        if (xhr.status == 200) {\n          resolve(true);\n        } else {\n          reject(new Error(\"Upload failed: \" + xhr.statusText));\n        }\n      };\n\n      // onabort triggers only when xhr.abort() is called\n      xhr.onabort = () => resolve(false);\n\n    });\n\n  }\n\n  stop() {\n    if (this.xhr) {\n      this.xhr.abort();\n    }\n  }\n\n}\n"
  },
  {
    "path": "5-network/10-long-polling/article.md",
    "content": "# Long polling\n\nLong polling is the simplest way of having persistent connection with server, that doesn't use any specific protocol like WebSocket or Server Side Events.\n\nBeing very easy to implement, it's also good enough in a lot of cases.\n\n## Regular Polling\n\nThe simplest way to get new information from the server is periodic polling. That is, regular requests to the server: \"Hello, I'm here, do you have any information for me?\". For example, once every 10 seconds.\n\nIn response, the server first takes a notice to itself that the client is online, and second - sends a packet of messages it got till that moment.\n\nThat works, but there are downsides:\n1. Messages are passed with a delay up to 10 seconds (between requests).\n2. Even if there are no messages, the server is bombed with requests every 10 seconds, even if the user switched somewhere else or is asleep. That's quite a load to handle, speaking performance-wise.\n\nSo, if we're talking about a very small service, the approach may be viable, but generally, it needs an improvement.\n\n## Long polling\n\nSo-called \"long polling\" is a much better way to poll the server.\n\nIt's also very easy to implement, and delivers messages without delays.\n\nThe flow:\n\n1. A request is sent to the server.\n2. The server doesn't close the connection until it has a message to send.\n3. When a message appears - the server responds to the request with it.\n4. The browser makes a new request immediately.\n\nThe situation when the browser sent a request and has a pending connection with the server, is standard for this method. Only when a message is delivered, the connection is reestablished.\n\n![](long-polling.svg)\n\nIf the connection is lost, because of, say, a network error, the browser immediately sends a new request.\n\nA sketch of client-side `subscribe` function that makes long requests:\n\n```js\nasync function subscribe() {\n  let response = await fetch(\"/subscribe\");\n\n  if (response.status == 502) {\n    // Status 502 is a connection timeout error,\n    // may happen when the connection was pending for too long,\n    // and the remote server or a proxy closed it\n    // let's reconnect\n    await subscribe();\n  } else if (response.status != 200) {\n    // An error - let's show it\n    showMessage(response.statusText);\n    // Reconnect in one second\n    await new Promise(resolve => setTimeout(resolve, 1000));\n    await subscribe();\n  } else {\n    // Get and show the message\n    let message = await response.text();\n    showMessage(message);\n    // Call subscribe() again to get the next message\n    await subscribe();\n  }\n}\n\nsubscribe();\n```\n\nAs you can see, `subscribe` function makes a fetch, then waits for the response, handles it and calls itself again.\n\n```warn header=\"Server should be ok with many pending connections\"\nThe server architecture must be able to work with many pending connections.\n\nCertain server architectures run one process per connection, resulting in there being as many processes as there are connections, while each process consumes quite a bit of memory. So, too many connections will just consume it all.\n\nThat's often the case for backends written in languages like PHP and Ruby.\n\nServers written using Node.js usually don't have such problems.\n\nThat said, it isn't a programming language issue. Most modern languages, including PHP and Ruby allow to implement a proper backend. Just please make sure that your server architecture works fine with many simultaneous connections.\n```\n\n## Demo: a chat\n\nHere's a demo chat, you can also download it and run locally (if you're familiar with Node.js and can install modules):\n\n[codetabs src=\"longpoll\" height=500]\n\nBrowser code is in `browser.js`.\n\n## Area of usage\n\nLong polling works great in situations when messages are rare.\n\nIf messages come very often, then the chart of requesting-receiving messages, painted above, becomes saw-like.\n\nEvery message is a separate request, supplied with headers, authentication overhead, and so on.\n\nSo, in this case, another method is preferred, such as [Websocket](info:websocket) or [Server Sent Events](info:server-sent-events).\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/browser.js",
    "content": "// Sending messages, a simple POST\nfunction PublishForm(form, url) {\n\n  function sendMessage(message) {\n    fetch(url, {\n      method: 'POST',\n      body: message\n    });\n  }\n\n  form.onsubmit = function() {\n    let message = form.message.value;\n    if (message) {\n      form.message.value = '';\n      sendMessage(message);\n    }\n    return false;\n  };\n}\n\n// Receiving messages with long polling\nfunction SubscribePane(elem, url) {\n\n  function showMessage(message) {\n    let messageElem = document.createElement('div');\n    messageElem.append(message);\n    elem.append(messageElem);\n  }\n\n  async function subscribe() {\n    let response = await fetch(url);\n\n    if (response.status == 502) {\n      // Connection timeout\n      // happens when the connection was pending for too long\n      // let's reconnect\n      await subscribe();\n    } else if (response.status != 200) {\n      // Show Error\n      showMessage(response.statusText);\n      // Reconnect in one second\n      await new Promise(resolve => setTimeout(resolve, 1000));\n      await subscribe();\n    } else {\n      // Got message\n      let message = await response.text();\n      showMessage(message);\n      await subscribe();\n    }\n  }\n\n  subscribe();\n\n}\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/index.html",
    "content": "<!DOCTYPE html>\n<script src=\"browser.js\"></script>\n\nAll visitors of this page will see messages of each other.\n\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\" />\n  <input type=\"submit\" value=\"Send\" />\n</form>\n\n<div id=\"subscribe\">\n</div>\n\n<script>\n  new PublishForm(document.forms.publish, 'publish');\n  // random url parameter to avoid any caching issues\n  new SubscribePane(document.getElementById('subscribe'), 'subscribe?random=' + Math.random());\n</script>\n"
  },
  {
    "path": "5-network/10-long-polling/longpoll.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\n\nlet fileServer = new static.Server('.');\n\nlet subscribers = Object.create(null);\n\nfunction onSubscribe(req, res) {\n  let id = Math.random();\n\n  res.setHeader('Content-Type', 'text/plain;charset=utf-8');\n  res.setHeader(\"Cache-Control\", \"no-cache, must-revalidate\");\n\n  subscribers[id] = res;\n\n  req.on('close', function() {\n    delete subscribers[id];\n  });\n\n}\n\nfunction publish(message) {\n\n  for (let id in subscribers) {\n    let res = subscribers[id];\n    res.end(message);\n  }\n\n  subscribers = Object.create(null);\n}\n\nfunction accept(req, res) {\n  let urlParsed = url.parse(req.url, true);\n\n  // new client wants messages\n  if (urlParsed.pathname == '/subscribe') {\n    onSubscribe(req, res);\n    return;\n  }\n\n  // sending a message\n  if (urlParsed.pathname == '/publish' && req.method == 'POST') {\n    // accept POST\n    req.setEncoding('utf8');\n    let message = '';\n    req.on('data', function(chunk) {\n      message += chunk;\n    }).on('end', function() {\n      publish(message); // publish it to everyone\n      res.end(\"ok\");\n    });\n\n    return;\n  }\n\n  // the rest is static\n  fileServer.serve(req, res);\n\n}\n\nfunction close() {\n  for (let id in subscribers) {\n    let res = subscribers[id];\n    res.end();\n  }\n}\n\n// -----------------------------------\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n  console.log('Server running on port 8080');\n} else {\n  exports.accept = accept;\n\n  if (process.send) { \n     process.on('message', (msg) => {\n       if (msg === 'shutdown') {\n         close();\n       }\n     });\n  }\n\n  process.on('SIGINT', close);\n}\n"
  },
  {
    "path": "5-network/11-websocket/article.md",
    "content": "# WebSocket\n\nThe `WebSocket` protocol, described in the specification [RFC 6455](http://tools.ietf.org/html/rfc6455) provides a way to exchange data between browser and server via a persistent connection. The data can be passed in both directions as \"packets\", without breaking the connection and additional HTTP-requests.\n\nWebSocket is especially great for services that require continuous data exchange, e.g. online games, real-time trading systems and so on.\n\n## A simple example\n\nTo open a websocket connection, we need to create `new WebSocket` using the special protocol `ws` in the url:\n\n```js\nlet socket = new WebSocket(\"*!*ws*/!*://javascript.info\");\n```\n\nThere's also encrypted `wss://` protocol. It's like HTTPS for websockets.\n\n```smart header=\"Always prefer `wss://`\"\nThe `wss://` protocol is not only encrypted, but also more reliable.\n\nThat's because `ws://` data is not encrypted, visible for any intermediary. Old proxy servers do not know about WebSocket, they may see \"strange\" headers and abort the connection.\n\nOn the other hand, `wss://` is WebSocket over TLS, (same as HTTPS is HTTP over TLS), the transport security layer encrypts the data at sender and decrypts at the receiver. So data packets are passed encrypted through proxies. They can't see what's inside and let them through.\n```\n\nOnce the socket is created, we should listen to events on it. There are totally 4 events:\n- **`open`** -- connection established,\n- **`message`** -- data received,\n- **`error`** -- websocket error,\n- **`close`** -- connection closed.\n\n...And if we'd like to send something, then `socket.send(data)` will do that.\n\nHere's an example:\n\n```js run\nlet socket = new WebSocket(\"wss://javascript.info/article/websocket/demo/hello\");\n\nsocket.onopen = function(e) {\n  alert(\"[open] Connection established\");\n  alert(\"Sending to server\");\n  socket.send(\"My name is John\");\n};\n\nsocket.onmessage = function(event) {\n  alert(`[message] Data received from server: ${event.data}`);\n};\n\nsocket.onclose = function(event) {\n  if (event.wasClean) {  \n    alert(`[close] Connection closed cleanly, code=${event.code} reason=${event.reason}`);\n  } else {\n    // e.g. server process killed or network down\n    // event.code is usually 1006 in this case\n    alert('[close] Connection died');\n  }\n};\n\nsocket.onerror = function(error) {\n  alert(`[error] ${error.message}`);\n};\n```\n\nFor demo purposes, there's a small server [server.js](demo/server.js) written in Node.js, for the example above, running. It responds with \"Hello from server, John\", then waits 5 seconds and closes the connection.\n\nSo you'll see events `open` -> `message` -> `close`.\n\nThat's actually it, we can talk WebSocket already. Quite simple, isn't it?\n\nNow let's talk more in-depth.\n\n## Opening a websocket\n\nWhen `new WebSocket(url)` is created, it starts connecting immediately.\n\nDuring the connection the browser (using headers) asks the server: \"Do you support Websocket?\" And if the server replies \"yes\", then the talk continues in WebSocket protocol, which is not HTTP at all.\n\n![](websocket-handshake.svg)\n\nHere's an example of browser headers for request made by `new WebSocket(\"wss://javascript.info/chat\")`.\n\n```\nGET /chat\nHost: javascript.info\nOrigin: https://javascript.info\nConnection: Upgrade\nUpgrade: websocket\nSec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==\nSec-WebSocket-Version: 13\n```\n\n- `Origin` -- the origin of the client page, e.g. `https://javascript.info`. WebSocket objects are cross-origin by nature. There are no special headers or other limitations. Old servers are unable to handle WebSocket anyway, so there are no compatibility issues. But `Origin` header is important, as it allows the server to decide whether or not to talk WebSocket with this website.\n- `Connection: Upgrade` -- signals that the client would like to change the protocol.\n- `Upgrade: websocket` -- the requested protocol is \"websocket\".\n- `Sec-WebSocket-Key` -- a random browser-generated key for security.\n- `Sec-WebSocket-Version` -- WebSocket protocol version, 13 is the current one.\n\n```smart header=\"WebSocket handshake can't be emulated\"\nWe can't use `XMLHttpRequest` or `fetch` to make this kind of HTTP-request, because JavaScript is not allowed to set these headers.\n```\n\nIf the server agrees to switch to WebSocket, it should send code 101 response:\n\n```\n101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=\n```\n\nHere `Sec-WebSocket-Accept` is `Sec-WebSocket-Key`, recoded using a special algorithm. The browser uses it to make sure that the response corresponds to the request.\n\nAfterwards, the data is transfered using WebSocket protocol, we'll see its structure (\"frames\") soon. And that's not HTTP at all.\n\n### Extensions and subprotocols\n\nThere may be additional headers `Sec-WebSocket-Extensions` and `Sec-WebSocket-Protocol` that describe extensions and subprotocols.\n\nFor instance:\n\n- `Sec-WebSocket-Extensions: deflate-frame` means that the browser supports data compression. An extension is something related to transferring the data, functionality that extends WebSocket protocol. The header `Sec-WebSocket-Extensions` is sent automatically by the browser, with the list of all extensions it supports.\n\n- `Sec-WebSocket-Protocol: soap, wamp` means that we'd like to transfer not just any data, but the data in [SOAP](http://en.wikipedia.org/wiki/SOAP) or WAMP (\"The WebSocket Application Messaging Protocol\") protocols. WebSocket subprotocols are registered in the [IANA catalogue](http://www.iana.org/assignments/websocket/websocket.xml). So, this header describes data formats that we're going to use.\n\n    This optional header is set using the second parameter of `new WebSocket`. That's the array of subprotocols, e.g. if we'd like to use SOAP or WAMP:\n\n    ```js\n    let socket = new WebSocket(\"wss://javascript.info/chat\", [\"soap\", \"wamp\"]);\n    ```\n\nThe server should respond with a list of protocols and extensions that it agrees to use.\n\nFor example, the request:\n\n```\nGET /chat\nHost: javascript.info\nUpgrade: websocket\nConnection: Upgrade\nOrigin: https://javascript.info\nSec-WebSocket-Key: Iv8io/9s+lYFgZWcXczP8Q==\nSec-WebSocket-Version: 13\n*!*\nSec-WebSocket-Extensions: deflate-frame\nSec-WebSocket-Protocol: soap, wamp\n*/!*\n```\n\nResponse:\n\n```\n101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\nSec-WebSocket-Accept: hsBlbuDTkk24srzEOTBUlZAlC2g=\n*!*\nSec-WebSocket-Extensions: deflate-frame\nSec-WebSocket-Protocol: soap\n*/!*\n```\n\nHere the server responds that it supports the extension \"deflate-frame\", and only SOAP of the requested subprotocols.\n\n## Data transfer\n\nWebSocket communication consists of \"frames\" -- data fragments, that can be sent from either side, and can be of several kinds:\n\n- \"text frames\" -- contain text data that parties send to each other.\n- \"binary data frames\" -- contain binary data that parties send to each other.\n- \"ping/pong frames\" are used to check the connection, sent from the server, the browser responds to these automatically.\n- there's also \"connection close frame\" and a few other service frames.\n\nIn the browser, we directly work only with text or binary frames.\n\n**WebSocket `.send()` method can send either text or binary data.**\n\nA call `socket.send(body)` allows `body` in string or a binary format, including `Blob`, `ArrayBuffer`, etc. No settings required: just send it out in any format.\n\n**When we receive the data, text always comes as string. And for binary data, we can choose between `Blob` and `ArrayBuffer` formats.**\n\nThat's set by `socket.binaryType` property, it's `\"blob\"` by default, so binary data comes as `Blob` objects.\n\n[Blob](info:blob) is a high-level binary object, it directly integrates with `<a>`, `<img>` and other tags, so that's a sane default. But for binary processing, to access individual data bytes, we can change it to `\"arraybuffer\"`:\n\n```js\nsocket.binaryType = \"arraybuffer\";\nsocket.onmessage = (event) => {\n  // event.data is either a string (if text) or arraybuffer (if binary)\n};\n```\n\n## Rate limiting\n\nImagine, our app is generating a lot of data to send. But the user has a slow network connection, maybe on a mobile internet, outside of a city.\n\nWe can call `socket.send(data)` again and again. But the data will be buffered (stored) in memory and sent out only as fast as network speed allows.\n\nThe `socket.bufferedAmount` property stores how many bytes remain buffered at this moment, waiting to be sent over the network.\n\nWe can examine it to see whether the socket is actually available for transmission.\n\n```js\n// every 100ms examine the socket and send more data  \n// only if all the existing data was sent out\nsetInterval(() => {\n  if (socket.bufferedAmount == 0) {\n    socket.send(moreData());\n  }\n}, 100);\n```\n\n\n## Connection close\n\nNormally, when a party wants to close the connection (both browser and server have equal rights), they send a \"connection close frame\" with a numeric code and a textual reason.\n\nThe method for that is:\n```js\nsocket.close([code], [reason]);\n```\n\n- `code` is a special WebSocket closing code (optional)\n- `reason` is a string that describes the reason of closing (optional)\n\nThen the other party in `close` event handler gets the code and the reason, e.g.:\n\n```js\n// closing party:\nsocket.close(1000, \"Work complete\");\n\n// the other party\nsocket.onclose = event => {\n  // event.code === 1000\n  // event.reason === \"Work complete\"\n  // event.wasClean === true (clean close)\n};\n```\n\nMost common code values:\n\n- `1000` -- the default, normal closure (used if no `code` supplied),\n- `1006` -- no way to set such code manually, indicates that the connection was lost (no close frame).\n\nThere are other codes like:\n\n- `1001` -- the party is going away, e.g. server is shutting down, or a browser leaves the page,\n- `1009` -- the message is too big to process,\n- `1011` -- unexpected error on server,\n- ...and so on.\n\nThe full list can be found in [RFC6455, §7.4.1](https://tools.ietf.org/html/rfc6455#section-7.4.1).\n\nWebSocket codes are somewhat like HTTP codes, but different. In particular, any codes less than `1000` are reserved, there'll be an error if we try to set such a code.\n\n```js\n// in case connection is broken\nsocket.onclose = event => {\n  // event.code === 1006\n  // event.reason === \"\"\n  // event.wasClean === false (no closing frame)\n};\n```\n\n\n## Connection state\n\nTo get connection state, additionally there's `socket.readyState` property with values:\n\n- **`0`** -- \"CONNECTING\": the connection has not yet been established,\n- **`1`** -- \"OPEN\": communicating,\n- **`2`** -- \"CLOSING\": the connection is closing,\n- **`3`** -- \"CLOSED\": the connection is closed.\n\n\n## Chat example\n\nLet's review a chat example using browser WebSocket API and Node.js WebSocket module <https://github.com/websockets/ws>. We'll pay the main attention to the client side, but the server is also simple.\n\nHTML: we need a `<form>` to send messages and a `<div>` for incoming messages:\n\n```html\n<!-- message form -->\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\">\n  <input type=\"submit\" value=\"Send\">\n</form>\n\n<!-- div with messages -->\n<div id=\"messages\"></div>\n```\n\nFrom JavaScript we want three things:\n1. Open the connection.\n2. On form submission -- `socket.send(message)` for the message.\n3. On incoming message -- append it to `div#messages`.\n\nHere's the code:\n\n```js\nlet socket = new WebSocket(\"wss://javascript.info/article/websocket/chat/ws\");\n\n// send message from the form\ndocument.forms.publish.onsubmit = function() {\n  let outgoingMessage = this.message.value;\n\n  socket.send(outgoingMessage);\n  return false;\n};\n\n// message received - show the message in div#messages\nsocket.onmessage = function(event) {\n  let message = event.data;\n\n  let messageElem = document.createElement('div');\n  messageElem.textContent = message;\n  document.getElementById('messages').prepend(messageElem);\n}\n```\n\nServer-side code is a little bit beyond our scope. Here we'll use Node.js, but you don't have to. Other platforms also have their means to work with WebSocket.\n\nThe server-side algorithm will be:\n\n1. Create `clients = new Set()` -- a set of sockets.\n2. For each accepted websocket, add it to the set `clients.add(socket)` and setup `message` event listener to get its messages.\n3. When a message received: iterate over clients and send it to everyone.\n4. When a connection is closed: `clients.delete(socket)`.\n\n```js\nconst ws = new require('ws');\nconst wss = new ws.Server({noServer: true});\n\nconst clients = new Set();\n\nhttp.createServer((req, res) => {\n  // here we only handle websocket connections\n  // in real project we'd have some other code here to handle non-websocket requests\n  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);\n});\n\nfunction onSocketConnect(ws) {\n  clients.add(ws);\n\n  ws.on('message', function(message) {\n    message = message.slice(0, 50); // max message length will be 50\n\n    for(let client of clients) {\n      client.send(message);\n    }\n  });\n\n  ws.on('close', function() {\n    clients.delete(ws);\n  });\n}\n```\n\n\nHere's the working example:\n\n[iframe src=\"chat\" height=\"100\" zip]\n\nYou can also download it (upper-right button in the iframe) and run locally. Just don't forget to install [Node.js](https://nodejs.org/en/) and `npm install ws` before running.\n\n## Summary\n\nWebSocket is a modern way to have persistent browser-server connections.\n\n- WebSockets don't have cross-origin limitations.\n- They are well-supported in browsers.\n- Can send/receive strings and binary data.\n\nThe API is simple.\n\nMethods:\n- `socket.send(data)`,\n- `socket.close([code], [reason])`.\n\nEvents:\n- `open`,\n- `message`,\n- `error`,\n- `close`.\n\nWebSocket by itself does not include reconnection, authentication and many other high-level mechanisms. So there are client/server libraries for that, and it's also possible to implement these capabilities manually.\n\nSometimes, to integrate WebSocket into existing project, people run WebSocket server in parallel with the main HTTP-server, and they share a single database. Requests to WebSocket use `wss://ws.site.com`, a subdomain that leads to WebSocket server, while `https://site.com` goes to the main HTTP-server.\n\nSurely, other ways of integration are also possible.\n"
  },
  {
    "path": "5-network/11-websocket/chat.view/index.html",
    "content": "<!doctype html>\n<form name=\"publish\">\n  <input type=\"text\" name=\"message\" maxlength=\"50\"/>\n  <input type=\"submit\" value=\"Send\"/>\n</form>\n\n<div id=\"messages\"></div>\n\n<script>\nlet url = location.host == 'localhost' ?\n  'ws://localhost:8080/ws' : location.host == 'javascript.local' ?\n  `ws://javascript.local/article/websocket/chat/ws` : // dev integration with local site\n  `wss://javascript.info/article/websocket/chat/ws`; // prod integration with javascript.info\n\nlet socket = new WebSocket(url);\n\n// send message from the form\ndocument.forms.publish.onsubmit = function() {\n  let outgoingMessage = this.message.value;\n\n  socket.send(outgoingMessage);\n  return false;\n};\n\n// handle incoming messages\nsocket.onmessage = function(event) {\n  let incomingMessage = event.data;\n  showMessage(incomingMessage);\n};\n\nsocket.onclose = event => console.log(`Closed ${event.code}`);\n\n// show message in div#messages\nfunction showMessage(message) {\n  let messageElem = document.createElement('div');\n  messageElem.textContent = message;\n  document.getElementById('messages').prepend(messageElem);\n}\n</script>\n"
  },
  {
    "path": "5-network/11-websocket/chat.view/server.js",
    "content": "/**\nBefore running:\n> npm install ws\nThen:\n> node server.js\n> open http://localhost:8080 in the browser\n*/\n\nconst http = require('http');\nconst fs = require('fs');\nconst ws = new require('ws');\n\nconst wss = new ws.Server({noServer: true});\n\nconst clients = new Set();\n\nfunction accept(req, res) {\n\n  if (req.url == '/ws' && req.headers.upgrade &&\n      req.headers.upgrade.toLowerCase() == 'websocket' &&\n      // can be Connection: keep-alive, Upgrade\n      req.headers.connection.match(/\\bupgrade\\b/i)) {\n    wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onSocketConnect);\n  } else if (req.url == '/') { // index.html\n    fs.createReadStream('./index.html').pipe(res);\n  } else { // page not found\n    res.writeHead(404);\n    res.end();\n  }\n}\n\nfunction onSocketConnect(ws) {\n  clients.add(ws);\n  log(`new connection`);\n\n  ws.on('message', function(message) {\n    log(`message received: ${message}`);\n\n    message = message.slice(0, 50); // max message length will be 50\n\n    for(let client of clients) {\n      client.send(message);\n    }\n  });\n\n  ws.on('close', function() {\n    log(`connection closed`);\n    clients.delete(ws);\n  });\n}\n\nlet log;\nif (!module.parent) {\n  log = console.log;\n  http.createServer(accept).listen(8080);\n} else {\n  // to embed into javascript.info\n  log = function() {};\n  // log = console.log;\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/11-websocket/demo.view/server.js",
    "content": "const http = require('http');\nconst ws = require('ws');\n\nconst wss = new ws.Server({noServer: true});\n\nfunction accept(req, res) {\n  // all incoming requests must be websockets\n  if (!req.headers.upgrade || req.headers.upgrade.toLowerCase() != 'websocket') {\n    res.end();\n    return;\n  }\n\n  // can be Connection: keep-alive, Upgrade\n  if (!req.headers.connection.match(/\\bupgrade\\b/i)) {\n    res.end();\n    return;\n  }\n\n  wss.handleUpgrade(req, req.socket, Buffer.alloc(0), onConnect);\n}\n\nfunction onConnect(ws) {\n  ws.on('message', function (message) {\n    let name = message.match(/([\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]+)$/gu) || \"Guest\";\n    ws.send(`Hello from server, ${name}!`);\n\n    setTimeout(() => ws.close(1000, \"Bye!\"), 5000);\n  });\n}\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/12-server-sent-events/article.md",
    "content": "# Server Sent Events\n\nThe [Server-Sent Events](https://html.spec.whatwg.org/multipage/comms.html#the-eventsource-interface) specification describes a built-in class `EventSource`, that keeps connection with the server and allows to receive events from it.\n\nSimilar to `WebSocket`, the connection is persistent.\n\nBut there are several important differences:\n\n| `WebSocket` | `EventSource` |\n|-------------|---------------|\n| Bi-directional: both client and server can exchange messages | One-directional: only server sends data |\n| Binary and text data | Only text |\n| WebSocket protocol | Regular HTTP |\n\n`EventSource` is a less-powerful way of communicating with the server than `WebSocket`.\n\nWhy should one ever use it?\n\nThe main reason: it's simpler. In many applications, the power of `WebSocket` is a little bit too much.\n\nWe need to receive a stream of data from server: maybe chat messages or market prices, or whatever. That's what `EventSource` is good at. Also it supports auto-reconnect, something  we need to implement manually with `WebSocket`. Besides, it's a plain old HTTP, not a new protocol.\n\n## Getting messages\n\nTo start receiving messages, we just need to create `new EventSource(url)`.\n\nThe browser will connect to `url` and keep the connection open, waiting for events.\n\nThe server should respond with status 200 and the header `Content-Type: text/event-stream`, then keep the connection and write messages into it in the special format, like this:\n\n```\ndata: Message 1\n\ndata: Message 2\n\ndata: Message 3\ndata: of two lines\n```\n\n- A message text goes after `data:`, the space after the colon is optional.\n- Messages are delimited with double line breaks `\\n\\n`.\n- To send a line break `\\n`, we can immediately send one more `data:` (3rd message above).\n\nIn practice, complex messages are usually sent JSON-encoded. Line-breaks are encoded as `\\n` within them, so multiline `data:` messages are not necessary.\n\nFor instance:\n\n```js\ndata: {\"user\":\"John\",\"message\":\"First line*!*\\n*/!* Second line\"}\n```\n\n...So we can assume that one `data:` holds exactly one message.\n\nFor each such message, the `message` event is generated:\n\n```js\nlet eventSource = new EventSource(\"/events/subscribe\");\n\neventSource.onmessage = function(event) {\n  console.log(\"New message\", event.data);\n  // will log 3 times for the data stream above\n};\n\n// or eventSource.addEventListener('message', ...)\n```\n\n### Cross-origin requests\n\n`EventSource` supports cross-origin requests, like `fetch` and any other networking methods. We can use any URL:\n\n```js\nlet source = new EventSource(\"https://another-site.com/events\");\n```\n\nThe remote server will get the `Origin` header and must respond with `Access-Control-Allow-Origin` to proceed.\n\nTo pass credentials, we should set the additional option `withCredentials`, like this:\n\n```js\nlet source = new EventSource(\"https://another-site.com/events\", {\n  withCredentials: true\n});\n```\n\nPlease see the chapter <info:fetch-crossorigin> for more details about cross-origin headers.\n\n\n## Reconnection\n\nUpon creation, `new EventSource` connects to the server, and if the connection is broken -- reconnects.\n\nThat's very convenient, as we don't have to care about it.\n\nThere's a small delay between reconnections, a few seconds by default.\n\nThe server can set the recommended delay using `retry:` in response (in milliseconds):\n\n```js\nretry: 15000\ndata: Hello, I set the reconnection delay to 15 seconds\n```\n\nThe `retry:` may come both together with some data, or as a standalone message.\n\nThe browser should wait that many milliseconds before reconnecting. Or longer, e.g. if the browser knows (from OS) that there's no network connection at the moment, it may wait until the connection appears, and then retry.\n\n- If the server wants the browser to stop reconnecting, it should respond with HTTP status 204.\n- If the browser wants to close the connection, it should call `eventSource.close()`:\n\n```js\nlet eventSource = new EventSource(...);\n\neventSource.close();\n```\n\nAlso, there will be no reconnection if the response has an incorrect `Content-Type` or its HTTP status differs from 301, 307, 200 and 204. In such cases the `\"error\"` event will be emitted, and the browser won't reconnect.\n\n```smart\nWhen a connection is finally closed, there's no way to \"reopen\" it. If we'd like to connect again, just create a new `EventSource`.\n```\n\n## Message id\n\nWhen a connection breaks due to network problems, either side can't be sure which messages were received, and which weren't.\n\nTo correctly resume the connection, each message should have an `id` field, like this:\n\n```\ndata: Message 1\nid: 1\n\ndata: Message 2\nid: 2\n\ndata: Message 3\ndata: of two lines\nid: 3\n```\n\nWhen a message with `id:` is received, the browser:\n\n- Sets the property `eventSource.lastEventId` to its value.\n- Upon reconnection sends the header `Last-Event-ID` with that `id`, so that the server may re-send following messages.\n\n```smart header=\"Put `id:` after `data:`\"\nPlease note: the `id` is appended below message `data` by the server, to ensure that `lastEventId` is updated after the message is received.\n```\n\n## Connection status: readyState\n\nThe `EventSource` object has `readyState` property, that has one of three values:\n\n```js no-beautify\nEventSource.CONNECTING = 0; // connecting or reconnecting\nEventSource.OPEN = 1;       // connected\nEventSource.CLOSED = 2;     // connection closed\n```\n\nWhen an object is created, or the connection is down, it's always `EventSource.CONNECTING` (equals `0`).\n\nWe can query this property to know the state of `EventSource`.\n\n## Event types\n\nBy default `EventSource` object generates three events:\n\n- `message` -- a message received, available as `event.data`.\n- `open` -- the connection is open.\n- `error` -- the connection could not be established, e.g. the server returned HTTP 500 status.\n\nThe server may specify another type of event with `event: ...` at the event start.\n\nFor example:\n\n```\nevent: join\ndata: Bob\n\ndata: Hello\n\nevent: leave\ndata: Bob\n```\n\nTo handle custom events, we must use `addEventListener`, not `onmessage`:\n\n```js\neventSource.addEventListener('join', event => {\n  alert(`Joined ${event.data}`);\n});\n\neventSource.addEventListener('message', event => {\n  alert(`Said: ${event.data}`);\n});\n\neventSource.addEventListener('leave', event => {\n  alert(`Left ${event.data}`);\n});\n```\n\n## Full example\n\nHere's the server that sends messages with `1`, `2`, `3`, then `bye` and breaks the connection.\n\nThen the browser automatically reconnects.\n\n[codetabs src=\"eventsource\"]\n\n## Summary\n\n`EventSource` object automatically establishes a persistent connection and allows the server to send messages over it.\n\nIt offers:\n- Automatic reconnect, with tunable `retry` timeout.\n- Message ids to resume events, the last received identifier is sent in `Last-Event-ID` header upon reconnection.\n- The current state is in the `readyState` property.\n\nThat makes `EventSource` a viable alternative to `WebSocket`, as the latter is more low-level and lacks such built-in features (though they can be implemented).\n\nIn many real-life applications, the power of `EventSource` is just enough.\n\nSupported in all modern browsers (not IE).\n\nThe syntax is:\n\n```js\nlet source = new EventSource(url, [credentials]);\n```\n\nThe second argument has only one possible option: `{ withCredentials: true }`, it allows sending cross-origin credentials.\n\nOverall cross-origin security is same as for `fetch` and other network methods.\n\n### Properties of an `EventSource` object\n\n`readyState`\n: The current connection state: either `EventSource.CONNECTING (=0)`, `EventSource.OPEN (=1)` or `EventSource.CLOSED (=2)`.\n\n`lastEventId`\n: The last received `id`. Upon reconnection the browser sends it in the header `Last-Event-ID`.\n\n### Methods\n\n`close()`\n: Closes the connection.\n\n### Events\n\n`message`\n: Message received, the data is in `event.data`.\n\n`open`\n: The connection is established.\n\n`error`\n: In case of an error, including both lost connection (will auto-reconnect) and fatal errors. We can check `readyState` to see if the reconnection is being attempted.\n\nThe server may set a custom event name in `event:`. Such events should be handled using `addEventListener`, not `on<event>`.\n\n### Server response format\n\nThe server sends messages, delimited by `\\n\\n`.\n\nA message may have following fields:\n\n- `data:` -- message body, a sequence of multiple `data` is interpreted as a single message, with `\\n` between the parts.\n- `id:` -- renews `lastEventId`, sent in `Last-Event-ID` on reconnect.\n- `retry:` -- recommends a retry delay for reconnections in ms. There's no way to set it from JavaScript.\n- `event:` -- event name, must precede `data:`.\n\nA message may include one or more fields in any order, but `id:` usually goes the last.\n"
  },
  {
    "path": "5-network/12-server-sent-events/eventsource.view/index.html",
    "content": "<!DOCTYPE html>\n<script>\nlet eventSource;\n\nfunction start() { // when \"Start\" button pressed\n  if (!window.EventSource) {\n    // IE or an old browser\n    alert(\"The browser doesn't support EventSource.\");\n    return;\n  }\n\n  eventSource = new EventSource('digits');\n\n  eventSource.onopen = function(e) {\n    log(\"Event: open\");\n  };\n\n  eventSource.onerror = function(e) {\n    log(\"Event: error\");\n    if (this.readyState == EventSource.CONNECTING) {\n      log(`Reconnecting (readyState=${this.readyState})...`);\n    } else {\n      log(\"Error has occured.\");\n    }\n  };\n\n  eventSource.addEventListener('bye', function(e) {\n    log(\"Event: bye, data: \" + e.data);\n  });\n\n  eventSource.onmessage = function(e) {\n    log(\"Event: message, data: \" + e.data);\n  };\n}\n\nfunction stop() { // when \"Stop\" button pressed\n  eventSource.close();\n  log(\"eventSource.close()\");\n}\n\nfunction log(msg) {\n  logElem.innerHTML += msg + \"<br>\";\n  document.documentElement.scrollTop = 99999999;\n}\n</script>\n\n<button onclick=\"start()\">Start</button> Press the \"Start\" to begin.\n<div id=\"logElem\" style=\"margin: 6px 0\"></div>\n\n<button onclick=\"stop()\">Stop</button> \"Stop\" to finish.\n"
  },
  {
    "path": "5-network/12-server-sent-events/eventsource.view/server.js",
    "content": "let http = require('http');\nlet url = require('url');\nlet querystring = require('querystring');\nlet static = require('node-static');\nlet fileServer = new static.Server('.');\n\nfunction onDigits(req, res) {\n  res.writeHead(200, {\n    'Content-Type': 'text/event-stream; charset=utf-8',\n    'Cache-Control': 'no-cache'\n  });\n\n  let i = 0;\n\n  let timer = setInterval(write, 1000);\n  write();\n\n  function write() {\n    i++;\n\n    if (i == 4) {\n      res.write('event: bye\\ndata: bye-bye\\n\\n');\n      clearInterval(timer);\n      res.end();\n      return;\n    }\n\n    res.write('data: ' + i + '\\n\\n');\n\n  }\n}\n\nfunction accept(req, res) {\n\n  if (req.url == '/digits') {\n    onDigits(req, res);\n    return;\n  }\n\n  fileServer.serve(req, res);\n}\n\n\nif (!module.parent) {\n  http.createServer(accept).listen(8080);\n} else {\n  exports.accept = accept;\n}\n"
  },
  {
    "path": "5-network/index.md",
    "content": "\n# Network requests\n"
  },
  {
    "path": "6-data-storage/01-cookie/article.md",
    "content": "# Cookies, document.cookie\n\nCookies are small strings of data that are stored directly in the browser. They are a part of the HTTP protocol, defined by the [RFC 6265](https://tools.ietf.org/html/rfc6265) specification.\n\nCookies are usually set by a web-server using the response `Set-Cookie` HTTP-header. Then, the browser automatically adds them to (almost) every request to the same domain using the `Cookie` HTTP-header.\n\nOne of the most widespread use cases is authentication:\n\n1. Upon sign in, the server uses the `Set-Cookie` HTTP-header in the response to set a cookie with a unique \"session identifier\".\n2. Next time when the request is sent to the same domain, the browser sends the cookie over the net using the `Cookie` HTTP-header.\n3. So the server knows who made the request.\n\nWe can also access cookies from the browser, using `document.cookie` property.\n\nThere are many tricky things about cookies and their options. In this chapter we'll cover them in detail.\n\n## Reading from document.cookie\n\n```online\nDoes your browser store any cookies from this site? Let's see:\n```\n\n```offline\nAssuming you're on a website, it's possible to see the cookies from it, like this:\n```\n\n```js run\n// At javascript.info, we use Google Analytics for statistics,\n// so there should be some cookies\nalert( document.cookie ); // cookie1=value1; cookie2=value2;...\n```\n\n\nThe value of `document.cookie` consists of `name=value` pairs, delimited by `; `. Each one is a separate cookie.\n\nTo find a particular cookie, we can split `document.cookie` by `; `, and then find the right name. We can use either a regular expression or array functions to do that.\n\nWe leave it as an exercise for the reader. Also, at the end of the chapter you'll find helper functions to manipulate cookies.\n\n## Writing to document.cookie\n\nWe can write to `document.cookie`. But it's not a data property, it's an accessor (getter/setter). An assignment to it is treated specially.\n\n**A write operation to `document.cookie` updates only cookies mentioned in it, but doesn't touch other cookies.**\n\nFor instance, this call sets a cookie with the name `user` and value `John`:\n\n```js run\ndocument.cookie = \"user=John\"; // update only cookie named 'user'\nalert(document.cookie); // show all cookies\n```\n\nIf you run it, then probably you'll see multiple cookies. That's because the `document.cookie=` operation does not overwrite all cookies. It only sets the mentioned cookie `user`.\n\nTechnically, name and value can have any characters. To keep the valid formatting, they should be escaped using a built-in `encodeURIComponent` function:\n\n```js run\n// special characters (spaces), need encoding\nlet name = \"my name\";\nlet value = \"John Smith\"\n\n// encodes the cookie as my%20name=John%20Smith\ndocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value);\n\nalert(document.cookie); // ...; my%20name=John%20Smith\n```\n\n\n```warn header=\"Limitations\"\nThere are few limitations:\n- The `name=value` pair, after `encodeURIComponent`, should not exceed 4KB. So we can't store anything huge in a cookie.\n- The total number of cookies per domain is limited to around 20+, the exact limit depends on the browser.\n```\n\nCookies have several options, many of them are important and should be set.\n\nThe options are listed after `key=value`, delimited by `;`, like this:\n\n```js run\ndocument.cookie = \"user=John; path=/; expires=Tue, 19 Jan 2038 03:14:07 GMT\"\n```\n\n## path\n\n- **`path=/mypath`**\n\nThe url path prefix must be absolute. It makes the cookie accessible for pages under that path. By default, it's the current path.\n\nIf a cookie is set with `path=/admin`, it's visible at pages `/admin` and `/admin/something`, but not at `/home` or `/adminpage`.\n\nUsually, we should set `path` to the root: `path=/` to make the cookie accessible from all website pages.\n\n## domain\n\n- **`domain=site.com`**\n\nA domain defines where the cookie is accessible. In practice though, there are limitations. We can't set any domain.\n\nBy default, a cookie is accessible only at the domain that set it. So, if the cookie was set by `site.com`, we won't get it at `other.com`.\n\n...But what's more tricky, we also won't get the cookie at a subdomain `forum.site.com`!\n\n```js\n// at site.com\ndocument.cookie = \"user=John\"\n\n// at forum.site.com\nalert(document.cookie); // no user\n```\n\n**There's no way to let a cookie be accessible from another 2nd-level domain, so `other.com` will never receive a cookie set at `site.com`.**\n\nIt's a safety restriction, to allow us to store sensitive data in cookies, that should be available only on one site.\n\n...But if we'd like to allow subdomains like `forum.site.com` to get a cookie, that's possible. When setting a cookie at `site.com`, we should explicitly set the `domain` option to the root domain: `domain=site.com`:\n\n```js\n// at site.com\n// make the cookie accessible on any subdomain *.site.com:\ndocument.cookie = \"user=John; domain=site.com\"\n\n// later\n\n// at forum.site.com\nalert(document.cookie); // has cookie user=John\n```\n\nFor historical reasons, `domain=.site.com` (a dot before `site.com`) also works the same way, allowing access to the cookie from subdomains. That's an old notation and should be used if we need to support very old browsers.\n\nSo, the `domain` option allows to make a cookie accessible at subdomains.\n\n## expires, max-age\n\nBy default, if a cookie doesn't have one of these options, it disappears when the browser is closed. Such cookies are called \"session cookies\"\n\nTo let cookies survive a browser close, we can set either the `expires` or `max-age` option.\n\n- **`expires=Tue, 19 Jan 2038 03:14:07 GMT`**\n\nThe cookie expiration date defines the time, when the browser will automatically delete it.\n\nThe date must be exactly in this format, in the GMT timezone. We can use `date.toUTCString` to get it. For instance, we can set the cookie to expire in 1 day:\n\n```js\n// +1 day from now\nlet date = new Date(Date.now() + 86400e3);\ndate = date.toUTCString();\ndocument.cookie = \"user=John; expires=\" + date;\n```\n\nIf we set `expires` to a date in the past, the cookie is deleted.\n\n-  **`max-age=3600`**\n\nIs an alternative to `expires` and specifies the cookie's expiration in seconds from the current moment.\n\nIf set to zero or a negative value, the cookie is deleted:\n\n```js\n// cookie will die in +1 hour from now\ndocument.cookie = \"user=John; max-age=3600\";\n\n// delete cookie (let it expire right now)\ndocument.cookie = \"user=John; max-age=0\";\n```\n\n## secure\n\n- **`secure`**\n\nThe cookie should be transferred only over HTTPS.\n\n**By default, if we set a cookie at `http://site.com`, then it also appears at `https://site.com` and vice versa.**\n\nThat is, cookies are domain-based, they do not distinguish between the protocols.\n\nWith this option, if a cookie is set by `https://site.com`, then it doesn't appear when the same site is accessed by HTTP, as `http://site.com`. So if a cookie has sensitive content that should never be sent over unencrypted HTTP, the `secure` flag is the right thing.\n\n```js\n// assuming we're on https:// now\n// set the cookie to be secure (only accessible over HTTPS)\ndocument.cookie = \"user=John; secure\";\n```  \n\n## samesite\n\nThat's another security attribute `samesite`. It's designed to protect from so-called XSRF (cross-site request forgery) attacks.\n\nTo understand how it works and when it's useful, let's take a look at XSRF attacks.\n\n### XSRF attack\n\nImagine, you are logged into the site `bank.com`. That is: you have an authentication cookie from that site. Your browser sends it to `bank.com` with every request, so that it recognizes you and performs all sensitive financial operations.\n\nNow, while browsing the web in another window, you accidentally come to another site `evil.com`. That site has JavaScript code that submits a form `<form action=\"https://bank.com/pay\">` to `bank.com` with fields that initiate a transaction to the hacker's account.\n\nThe browser sends cookies every time you visit the site `bank.com`, even if the form was submitted from `evil.com`. So the bank recognizes you and actually performs the payment.\n\n![](cookie-xsrf.svg)\n\nThat's a so-called \"Cross-Site Request Forgery\" (in short, XSRF) attack.\n\nReal banks are protected from it of course. All forms generated by `bank.com` have a special field, a so-called \"XSRF protection token\", that an evil page can't generate or extract from a remote page. It can submit a form there, but can't get the data back. The site `bank.com` checks for such token in every form it receives.\n\nSuch a protection takes time to implement though. We need to ensure that every form has the required token field, and we must also check all requests.\n\n### Enter cookie samesite option\n\nThe cookie `samesite` option provides another way to protect from such attacks, that (in theory) should not require \"xsrf protection tokens\".\n\nIt has two possible values:\n\n- **`samesite=strict` (same as `samesite` without value)**\n\nA cookie with `samesite=strict` is never sent if the user comes from outside the same site.\n\nIn other words, whether a user follows a link from their mail or submits a form from `evil.com`, or does any operation that originates from another domain, the cookie is not sent.\n\nIf authentication cookies have the `samesite` option, then a XSRF attack has no chances to succeed, because a submission from `evil.com` comes without cookies. So `bank.com` will not recognize the user and will not proceed with the payment.\n\nThe protection is quite reliable. Only operations that come from `bank.com` will send the `samesite` cookie, e.g. a form submission from another page at `bank.com`.\n\nAlthough, there's a small inconvenience.\n\nWhen a user follows a legitimate link to `bank.com`, like from their own notes, they'll be surprised that `bank.com` does not recognize them. Indeed, `samesite=strict` cookies are not sent in that case.\n\nWe could work around that by using two cookies: one for \"general recognition\", only for the purposes of saying: \"Hello, John\", and the other one for data-changing operations with `samesite=strict`. Then, a person coming from outside of the site will see a welcome, but payments must be initiated from the bank's website, for the second cookie to be sent.\n\n- **`samesite=lax`**\n\nA more relaxed approach that also protects from XSRF and doesn't break the user experience.\n\nLax mode, just like `strict`, forbids the browser to send cookies when coming from outside the site, but adds an exception.\n\nA `samesite=lax` cookie is sent if both of these conditions are true:\n1. The HTTP method is \"safe\" (e.g. GET, but not POST).\n\n    The full list of safe HTTP methods is in the [RFC7231 specification](https://tools.ietf.org/html/rfc7231). Basically, these are the methods that should be used for reading, but not writing the data. They must not perform any data-changing operations. Following a link is always GET, the safe method.\n\n2. The operation performs a top-level navigation (changes URL in the browser address bar).\n\n    That's usually true, but if the navigation is performed in an `<iframe>`, then it's not top-level. Also, JavaScript methods for network requests do not perform any navigation, hence they don't fit.\n\nSo, what `samesite=lax` does, is to basically allow the most common \"go to URL\" operation to have cookies. E.g. opening a website link from notes that satisfy these conditions.\n\nBut anything more complicated, like a network request from another site or a form submission, loses cookies.\n\nIf that's fine for you, then adding `samesite=lax` will probably not break the user experience and add protection.\n\nOverall, `samesite` is a great option. \n\nThere's a drawback:\n\n- `samesite` is ignored (not supported) by very old browsers, year 2017 or so.\n\n**So if we solely rely on `samesite` to provide protection, then old browsers will be vulnerable.**\n\nBut we surely can use `samesite` together with other protection measures, like xsrf tokens, to add an additional layer of defence and then, in the future, when old browsers die out, we'll probably be able to drop xsrf tokens.\n\n## httpOnly\n\nThis option has nothing to do with JavaScript, but we have to mention it for completeness.\n\nThe web-server uses the `Set-Cookie` header to set a cookie. Also, it may set the `httpOnly` option.\n\nThis option forbids any JavaScript access to the cookie. We can't see such a cookie or manipulate it using `document.cookie`.\n\nThat's used as a precaution measure, to protect from certain attacks when a hacker injects his own JavaScript code into a page and waits for a user to visit that page. That shouldn't be possible at all, hackers should not be able to inject their code into our site, but there may be bugs that let them do it.\n\n\nNormally, if such a thing happens, and a user visits a web-page with hacker's JavaScript code, then that code executes and gains access to `document.cookie` with user cookies containing authentication information. That's bad.\n\nBut if a cookie is `httpOnly`, then `document.cookie` doesn't see it, so it is protected.\n\n## Appendix: Cookie functions\n\nHere's a small set of functions to work with cookies, more convenient than a manual modification of `document.cookie`.\n\nThere exist many cookie libraries for that, so these are for demo purposes. Fully working though.\n\n\n### getCookie(name)\n\nThe shortest way to access a cookie is to use a [regular expression](info:regular-expressions).\n\nThe function `getCookie(name)` returns the cookie with the given `name`:\n\n```js\n// returns the cookie with the given name,\n// or undefined if not found\nfunction getCookie(name) {\n  let matches = document.cookie.match(new RegExp(\n    \"(?:^|; )\" + name.replace(/([\\.$?*|{}\\(\\)\\[\\]\\\\\\/\\+^])/g, '\\\\$1') + \"=([^;]*)\"\n  ));\n  return matches ? decodeURIComponent(matches[1]) : undefined;\n}\n```\n\nHere `new RegExp` is generated dynamically, to match `; name=<value>`.\n\nPlease note that a cookie value is encoded, so `getCookie` uses a built-in `decodeURIComponent` function to decode it.\n\n### setCookie(name, value, options)\n\nSets the cookie's `name` to the given `value` with `path=/` by default (can be modified to add other defaults):\n\n```js run\nfunction setCookie(name, value, options = {}) {\n\n  options = {\n    path: '/',\n    // add other defaults here if necessary\n    ...options\n  };\n\n  if (options.expires instanceof Date) {\n    options.expires = options.expires.toUTCString();\n  }\n\n  let updatedCookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value);\n\n  for (let optionKey in options) {\n    updatedCookie += \"; \" + optionKey;\n    let optionValue = options[optionKey];\n    if (optionValue !== true) {\n      updatedCookie += \"=\" + optionValue;\n    }\n  }\n\n  document.cookie = updatedCookie;\n}\n\n// Example of use:\nsetCookie('user', 'John', {secure: true, 'max-age': 3600});\n```\n\n### deleteCookie(name)\n\nTo delete a cookie, we can call it with a negative expiration date:\n\n```js\nfunction deleteCookie(name) {\n  setCookie(name, \"\", {\n    'max-age': -1\n  })\n}\n```\n\n```warn header=\"Updating or deleting must use same path and domain\"\nPlease note: when we update or delete a cookie, we should use exactly the same path and domain options as when we set it.\n```\n\nTogether: [cookie.js](cookie.js).\n\n\n## Appendix: Third-party cookies\n\nA cookie is called \"third-party\" if it's placed by a domain other than the page the user is visiting.\n\nFor instance:\n1. A page at `site.com` loads a banner from another site: `<img src=\"https://ads.com/banner.png\">`.\n2. Along with the banner, the remote server at `ads.com` may set the `Set-Cookie` header with a cookie like `id=1234`. Such a cookie originates from the `ads.com` domain, and will only be visible at `ads.com`:\n\n    ![](cookie-third-party.svg)\n\n3. Next time when `ads.com` is accessed, the remote server gets the `id` cookie and recognizes the user:\n\n    ![](cookie-third-party-2.svg)\n\n4. What's even more important is, when the user moves from `site.com` to another site `other.com`, which also has a banner, then `ads.com` gets the cookie, as it belongs to `ads.com`, thus recognizing the visitor and tracking him as he moves between sites:\n\n    ![](cookie-third-party-3.svg)\n\n\nThird-party cookies are traditionally used for tracking and ads services, due to their nature. They are bound to the originating domain, so `ads.com` can track the same user between different sites, if they all access it.\n\nNaturally, some people don't like being tracked, so browsers allow to disable such cookies.\n\nAlso, some modern browsers employ special policies for such cookies:\n- Safari does not allow third-party cookies at all.\n- Firefox comes with a \"black list\" of third-party domains where it blocks third-party cookies.\n\n\n```smart\nIf we load a script from a third-party domain, like `<script src=\"https://google-analytics.com/analytics.js\">`, and that script uses `document.cookie` to set a cookie, then such cookie is not third-party.\n\nIf a script sets a cookie, then no matter where the script came from -- the cookie belongs to the domain of the current webpage.\n```\n\n## Appendix: GDPR\n\nThis topic is not related to JavaScript at all, just something to keep in mind when setting cookies.\n\nThere's a legislation in Europe called GDPR, that enforces a set of rules for websites to respect the users' privacy. One of these rules is to require an explicit permission for tracking cookies from the user.\n\nPlease note, that's only about tracking/identifying/authorizing cookies.\n\nSo, if we set a cookie that just saves some information, but neither tracks nor identifies the user, then we are free to do it.\n\nBut if we are going to set a cookie with an authentication session or a tracking id, then a user must allow that.\n\nWebsites generally have two variants of following GDPR. You must have seen them both already in the web:\n\n1. If a website wants to set tracking cookies only for authenticated users.\n\n    To do so, the registration form should have a checkbox like \"accept the privacy policy\" (that describes how cookies are used), the user must check it, and then the website is free to set auth cookies.\n\n2. If a website wants to set tracking cookies for everyone.\n\n    To do so legally, a website shows a modal \"splash screen\" for newcomers, and requires them to agree to the cookies. Then the website can set them and let people see the content. That can be disturbing for new visitors though. No one likes to see such \"must-click\" modal splash screens instead of the content. But GDPR requires an explicit agreement.\n\n\nGDPR is not only about cookies, it's about other privacy-related issues too, but that's too much beyond our scope.\n\n\n## Summary\n\n`document.cookie` provides access to cookies\n- write operations modify only cookies mentioned in it.\n- name/value must be encoded.\n- one cookie must not exceed 4KB, 20+ cookies per site (depends on the browser).\n\nCookie options:\n- `path=/`, by default current path, makes the cookie visible only under that path.\n- `domain=site.com`, by default a cookie is visible on the current domain only. If the domain is set explicitly, the cookie becomes visible on subdomains.\n- `expires` or `max-age` sets the cookie expiration time. Without them the cookie dies when the browser is closed.\n- `secure` makes the cookie HTTPS-only.\n- `samesite` forbids the browser to send the cookie with requests coming from outside the site. This helps to prevent XSRF attacks.\n\nAdditionally:\n- Third-party cookies may be forbidden by the browser, e.g. Safari does that by default.\n- When setting a tracking cookie for EU citizens, GDPR requires to ask for permission.\n"
  },
  {
    "path": "6-data-storage/01-cookie/cookie.js",
    "content": "function getCookie(name) {\n  let matches = document.cookie.match(new RegExp(\n    \"(?:^|; )\" + name.replace(/([\\.$?*|{}\\(\\)\\[\\]\\\\\\/\\+^])/g, '\\\\$1') + \"=([^;]*)\"\n  ));\n  return matches ? decodeURIComponent(matches[1]) : undefined;\n}\n\nfunction setCookie(name, value, options = {}) {\n\n  options = {\n    path: '/',\n    // add other defaults here if necessary\n    ...options\n  };\n\n  if (options.expires instanceof Date) {\n    options.expires = options.expires.toUTCString();\n  }\n\n  let updatedCookie = encodeURIComponent(name) + \"=\" + encodeURIComponent(value);\n\n  for (let optionKey in options) {\n    updatedCookie += \"; \" + optionKey;\n    let optionValue = options[optionKey];\n    if (optionValue !== true) {\n      updatedCookie += \"=\" + optionValue;\n    }\n  }\n\n  document.cookie = updatedCookie;\n}\n\n\nfunction deleteCookie(name) {\n  setCookie(name, \"\", {\n    'max-age': -1\n  })\n}\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/solution.md",
    "content": "\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/solution.view/index.html",
    "content": "<!doctype html>\n<textarea style=\"width:200px; height: 60px;\" id=\"area\" placeholder=\"Write here\"></textarea>\n<br>\n<button onclick=\"localStorage.removeItem('area');area.value=''\">Clear</button>\n<script>\n    area.value = localStorage.getItem('area');\n    area.oninput = () => {\n      localStorage.setItem('area', area.value)\n    };\n</script>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/source.view/index.html",
    "content": "<!doctype html>\n<textarea style=\"width:200px; height: 60px;\" id=\"area\"></textarea>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/1-form-autosave/task.md",
    "content": "\n# Autosave a form field\n\nCreate a `textarea` field that \"autosaves\" its value on every change.\n\nSo, if the user accidentally closes the page, and opens it again, he'll find his unfinished input at place.\n\nLike this:\n\n[iframe src=\"solution\" height=120]\n"
  },
  {
    "path": "6-data-storage/02-localstorage/article.md",
    "content": "# LocalStorage, sessionStorage\n\nObjek penyimpanan web `localStorage` dan `sessionStorage` memungkinkan untuk menyimpan pasangan *key*/*value* di peramban.\n\nYang menarik dari mereka adalah bahwa data bertahan dari refresh halaman (untuk `sessionStorage`) dan bahkan ketika peramban dimulai ulang secara penuh (untuk `localStorage`). Kita akan melihatnya segera.\n\nKita sudah memiliki cookies. Mengapa perlu tambahan objek?\n\n- Tidak seperti cookies, objek penyimpanan web tidak dikirim ke server pada setiap permintaan. Karena itu, kita bisa menyimpan lebih banyak. Sebagian besar peramban mengizinkan setidaknya 2 *megabyte* data (atau lebih) dan memiliki pengaturan untuk konfigurasinya.\n- Juga tidak seperti cookies, server tidak dapat memanipulasi objek penyimpanan melalui HTTP header. Semuanya dilakukan dalam JavaScript.\n- Penyimpanan terikat kepada asalnya (domain/protokol/port triplet). Artinya, protokol atau subdomain yang berbeda menyimpulkan objek penyimpanan yang berbeda, mereka tidak dapat mengakses data satu sama lain.\n\nKedua objek penyimpanan menyediakan *method* dan properti yang sama:\n\n- `setItem(key, value)` -- menyimpan pasangan *key*/*value*.\n- `getItem(key)` -- mendapatkan *value* dengan sebuah *key*.\n- `removeItem(key)` -- menghapus *key* beserta *value*-nya.\n- `clear()` -- menghapus semuanya.\n- `key(index)` -- mendapatkan *key* pada posisi tertentu.\n- `length` -- jumlah item yang disimpan.\n\nSeperti yang Anda lihat, ini seperti koleksi `Map` (`setItem/getItem/removeItem`), tetapi juga memungkinkan akses berdasarkan indeks dengan `key(index)`.\n\nMari kita lihat cara kerjanya.\n\n## Demo localStorage\n\nFitur utama `localStorage` adalah:\n\n- Dibagikan antara semua *tab* dan *window* dari *origin* yang sama.\n- Data tidak kedaluwarsa. Data tetap tersimpan setelah peramban *restart* dan bahkan setelah OS *reboot*.\n\nMisalnya, jika Anda menjalankan kode ini...\n\n```js run\nlocalStorage.setItem('test', 1);\n```\n\n...Dan kemudian menutup/membuka peramban atau hanya membuka halaman yang sama di jendela yang berbeda, maka Anda bisa mendapatkannya seperti ini:\n\n```js run\nalert( localStorage.getItem('test') ); // 1\n```\n\nKita hanya harus berada di *origin* yang sama (domain/port/protocol), *path* urlnya bisa berbeda.\n\n`localStorage` dibagi antara semua *window* dengan *origin* yang sama, jadi jika kita mengatur data di satu *window*, perubahan akan terlihat di *window* lain.\n\n## Akses seperti objek\n\nKita juga dapat menggunakan cara sebuah objek biasa untuk mendapatkan/mengatur *key*, seperti ini:\n\n```js run\n// mengatur key\nlocalStorage.test = 2;\n\n// mendapatkan key\nalert( localStorage.test ); // 2\n\n// menghapus key\ndelete localStorage.test;\n```\n\nItu diizinkan karena alasan historis, dan sebagian besar berfungsi, tetapi umumnya tidak disarankan, karena:\n\n1. Jika *key* dibuat oleh pengguna, itu bisa apa saja, seperti `length` atau `toString`, atau *method* bawaan `localStorage` lainnya. Dalam hal itu `getItem/setItem` berfungsi dengan baik, sementara akses seperti objek akan gagal:\n    ```js run\n    let key = 'length';\n    localStorage[key] = 5; // Error, can't assign length\n    ```\n2. Ada *event* `storage`, yang dipicu saat kita mengubah data. *Event* itu tidak terjadi untuk akses seperti objek. Kita akan melihatnya nanti di bab ini.\n\n## Pengulangan pada keys\n\nSeperti yang telah kita lihat, *method* menyediakan fungsionalitas \"mendapatkan/mengatur/menghapus dengan *key*\". Tetapi bagaimana cara mendapatkan semua *value* atau *key* yang disimpan?\n\nSayangnya, objek penyimpanan tidak bisa dilakukan iterasi.\n\nSalah satu caranya adalah dengan mengulangnya sebagai senarai (array):\n\n```js run\nfor(let i=0; i<localStorage.length; i++) {\n  let key = localStorage.key(i);\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\nCara lain adalah dengan menggunakan perulangan `for key in localStorage`, seperti yang kita lakukan dengan objek reguler.\n\nIni melakukan iterasi pada *key*, tetapi juga menampilkan beberapa *field* bawaan yang tidak kita perlukan:\n\n```js run\n// Percobaan yang buruk\nfor(let key in localStorage) {\n  alert(key); // menampilkan getItem, setItem and hal bawaan lainnya\n}\n```\n\n...Jadi kita perlu memfilter *field* dari *prototype* dengan pemeriksaan `hasOwnProperty`:\n```js run\nfor(let key in localStorage) {\n  if (!localStorage.hasOwnProperty(key)) {\n    continue; // melewati keys seperti \"setItem\", \"getItem\" etc\n  }\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\n...Atau jika hanya mendapatkan *key*-nya saja dengan `Object.keys` dan kemudian dilakukan perulangan jika diperlukan:\n\n```js run\nlet keys = Object.keys(localStorage);\nfor(let key of keys) {\n  alert(`${key}: ${localStorage.getItem(key)}`);\n}\n```\n\nYang terakhir berfungsi, karena `Object.keys` hanya mengembalikan kunci milik objek, mengabaikan *prototype*.\n\n## Hanya string\n\nHarap dicatat bahwa *key* dan *value* harus berupa string.\n\nJika ada tipe lain, seperti angka, atau objek, itu akan dikonversi menjadi string secara otomatis:\n\n```js run\nlocalStorage.user = {name: \"John\"};\nalert(localStorage.user); // [object Object]\n```\n\nKita dapat menggunakan `JSON` untuk menyimpan objek:\n\n```js run\nlocalStorage.user = JSON.stringify({name: \"John\"});\n\n// kemudian\nlet user = JSON.parse( localStorage.user );\nalert( user.name ); // John\n```\n\nJuga dimungkinkan untuk *stringify* seluruh objek penyimpanan, sebagai contoh untuk tujuan *debugging*:\n\n```js run\n// menambahkan opsi pemformatan pada JSON.stringify untuk membuat objek terlihat lebih bagus\nalert( JSON.stringify(localStorage, null, 2) );\n```\n\n\n## sessionStorage\n\nObjek `sessionStorage` lebih jarang digunakan daripada `localStorage`.\n\nProperti dan *method*-nya sama, tetapi jauh lebih terbatas:\n\n- `sessionStorage` hanya ada di dalam tab peramban saat ini.\n  - Tab lain dengan halaman yang sama akan memiliki penyimpanan yang berbeda.\n  - Tapi dibagi antar iframe di tab yang sama (dengan asumsi mereka berasal dari *origin* yang sama).\n- Data bertahan dari *refresh* halaman, tetapi tidak dengan menutup/membuka tab.\n\nMari kita lihat langsung prakteknya.\n\nJalankan kode ini...\n\n```js run\nsessionStorage.setItem('test', 1);\n```\n\n...Kemudian *refresh* halaman. Sekarang Anda masih bisa mendapatkan data:\n\n```js run\nalert( sessionStorage.getItem('test') ); // setelah refresh: 1\n```\n\n...Tetapi jika Anda membuka halaman yang sama di tab lain, dan mencoba lagi di sana, kode di atas mengembalikan `null`, yang berarti \"tidak ada yang ditemukan\".\n\nItu persis terjadi karena `sessionStorage` terikat tidak hanya pada *origin*, tetapi juga pada tab peramban. Oleh karena itu, `sessionStorage` jarang digunakan\n\n## Event storage\n\nSaat data diperbarui di `localStorage` atau `sessionStorage`, *event* [storage](https://www.w3.org/TR/webstorage/#the-storage-event) terpicu, dengan properti:\n\n- `key` – *key* yang diubah (`null` jika `.clear()` dipanggil).\n- `oldValue` – *value* lama (`null` jika *key* baru ditambahkan).\n- `newValue` – *value* baru (`null` jika key dihapus).\n- `url` – url dokumen tempat pembaruan terjadi.\n- `storageArea` – objek `localStorage` atau `sessionStorage` tempat pembaruan terjadi.\n\nYang penting adalah: *event* terpicu pada semua objek `window` tempat penyimpanan dapat diakses, kecuali yang menyebabkannya.\n\nMari kita perjelas.\n\nBayangkan, Anda memiliki dua *window* dengan situs yang sama di masing-masing *window*. Jadi `localStorage` dibagi di antara keduanya.\n\n```online\nAnda mungkin ingin membuka halaman ini di dua window peramban untuk menguji kode di bawah ini.\n```\n\nJika kedua *window* mendengarkan (listening) `window.onstorage`, maka masing-masing akan bereaksi terhadap pembaruan yang terjadi di *window* lainnya.\n\n```js run\n// memicu pembaruan yang dibuat ke penyimpanan yang sama dari dokumen lain\nwindow.onstorage = event => { //sama seperti window.addEventListener('storage', event => {\n  if (event.key != 'now') return;\n  alert(event.key + ':' + event.newValue + \" at \" + event.url);\n};\n\nlocalStorage.setItem('now', Date.now());\n```\n\nHarap perhatikan bahwa *event* juga berisi: `event.url` -- url dokumen tempat data diperbarui.\n\nSelain itu, `event.storageArea` berisi objek penyimpanan -- *event*-nya sama untuk `sessionStorage` dan `localStorage`, jadi `event.storageArea` merujuk ke yang telah dimodifikasi. Kita bahkan mungkin ingin mengatur sesuatu kembali di dalamnya, untuk \"merespons\" perubahan.\n\n**Itu memungkinkan window yang berbeda dari origin yang sama untuk bertukar pesan.**\n\nPeramban modern juga mendukung [Broadcast channel API](mdn:/api/Broadcast_Channel_API), API khusus untuk komunikasi antar-jendela dengan origin yang sama, fiturnya lebih lengkap, tetapi kurang didukung. Ada *libraries* yang melakukan polifill API tersebut, berdasarkan `localStorage`, yang membuatnya tersedia di mana saja.\n\n## Ringkasan\n\nObjek penyimpanan web `localStorage` dan `sessionStorage` memungkinkan untuk menyimpan *key*/*value* di peramban.\n- Baik `key` dan `value` harus berupa string.\n- Batasnya adalah 5mb+, tergantung pada peramban.\n- Mereka tidak kedaluwarsa.\n- Data terikat pada *origin* (domain/port/protokol).\n\n| `localStorage` | `sessionStorage` |\n|----------------|------------------|\n| Dibagikan antara semua tab dan window dengan origin yang sama | Terlihat dalam tab browser, termasuk iframe dari origin yang sama |\n| Bertahan dari restart peramban | Bertahan dari refresh halaman (tetapi tidak dengan menutup tab) |\n\nAPI:\n\n- `setItem(key, value)` -- menyimpan pasangan *key*/*value*. \n- `getItem(key)` -- mendapatkan *value* dengan *key*.\n- `removeItem(key)` -- menghapus *key* beserta *value*-nya.\n- `clear()` -- menghapus semuanya.\n- `key(index)` -- mendapatkan nomor *key* `index`.\n- `length` -- jumlah item yang disimpan. \n- Gunakan `Object.keys` untuk mendapatkan semua *key*.\n- Kita mengakses *key* sebagai properti objek, dalam hal ini *event* `storage` tidak dipicu.\n\n*Event storage*:\n\n- Pemicu pada panggilan `setItem`, `removeItem`, `clear`.\n- Berisi semua data tentang operasi (`key/oldValue/newValue`), dokumen `url` dan objek penyimpanan `storageArea`.\n- Memicu semua objek `window` yang memiliki akses ke penyimpanan kecuali yang membuatnya (dalam tab untuk `sessionStorage`, secara global untuk `localStorage`).\n"
  },
  {
    "path": "6-data-storage/02-localstorage/sessionstorage.view/iframe.html",
    "content": "<!doctype html>\n<script>\n  window.addEventListener('storage', event => {\n    alert(\"iframe.html: onstorage\");\n  });\n</script>\n<button onclick=\"sessionStorage.setItem('now', new Date())\">sessionStorage.setItem</button>\n</body>\n</html>\n"
  },
  {
    "path": "6-data-storage/02-localstorage/sessionstorage.view/index.html",
    "content": "<!doctype html>\n<script>\n  window.addEventListener('storage', event => {\n    alert(\"index.html: onstorage\");\n  });\n</script>\n<button onclick=\"sessionStorage.setItem('now', new Date())\">sessionStorage.setItem</button>\n<iframe src=\"iframe.html\" style=\"height:100px\"></iframe>\n</body>\n</html>\n"
  },
  {
    "path": "6-data-storage/03-indexeddb/article.md",
    "content": "libs:\n  - 'https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js'\n\n---\n\n# IndexedDB\n\nIndexedDB is a database that is built into a browser, much more powerful than `localStorage`.\n\n- Stores almost any kind of values by keys, multiple key types.\n- Supports transactions for reliability.\n- Supports key range queries, indexes.\n- Can store much bigger volumes of data than `localStorage`.\n\nThat power is usually excessive for traditional client-server apps. IndexedDB is intended for offline apps, to be combined with ServiceWorkers and other technologies.\n\nThe native interface to IndexedDB, described in the specification <https://www.w3.org/TR/IndexedDB>, is event-based.\n\nWe can also use `async/await` with the help of a promise-based wrapper, like <https://github.com/jakearchibald/idb>. That's pretty convenient, but the wrapper is not perfect, it can't replace events for all cases. So we'll start with events, and then, after we gain an understanding of IndexedDb, we'll use the wrapper.\n\n```smart header=\"Where's the data?\"\nTechnically, the data is usually stored in the visitor's home directory, along with browser settings, extensions, etc.\n\nDifferent browsers and OS-level users have each their own independant storage.\n```\n\n## Open database\n\nTo start working with IndexedDB, we first need to `open` (connect to) a database.\n\nThe syntax:\n\n```js\nlet openRequest = indexedDB.open(name, version);\n```\n\n- `name` -- a string, the database name.\n- `version` -- a positive integer version, by default `1` (explained below).\n\nWe can have many databases with different names, but all of them exist within the current origin (domain/protocol/port). Different websites can't access each other's databases.\n\nThe call returns `openRequest` object, we should listen to events on it:\n- `success`: database is ready, there's the \"database object\" in `openRequest.result`, we should use it for further calls.\n- `error`: opening failed.\n- `upgradeneeded`: database is ready, but its version is outdated (see below).\n\n**IndexedDB has a built-in mechanism of \"schema versioning\", absent in server-side databases.**\n\nUnlike server-side databases, IndexedDB is client-side, the data is stored in the browser, so we, developers, don't have full-time access to it. So, when we have published a new version of our app, and the user visits our webpage, we may need to update the database.\n\nIf the local database version is less than specified in `open`, then a special event `upgradeneeded` is triggered, and we can compare versions and upgrade data structures as needed.\n\nThe `upgradeneeded` event also triggers when the database doesn't yet exist (technically, its version is `0`), so we can perform the initialization.\n\nLet's say we published the first version of our app.\n\nThen we can open the database with version `1` and perform the initialization in an `upgradeneeded` handler like this:\n\n```js\nlet openRequest = indexedDB.open(\"store\", *!*1*/!*);\n\nopenRequest.onupgradeneeded = function() {\n  // triggers if the client had no database\n  // ...perform initialization...\n};\n\nopenRequest.onerror = function() {\n  console.error(\"Error\", openRequest.error);\n};\n\nopenRequest.onsuccess = function() {\n  let db = openRequest.result;\n  // continue working with database using db object\n};\n```\n\nThen, later, we publish the 2nd version.\n\nWe can open it with version `2` and perform the upgrade like this:\n\n```js\nlet openRequest = indexedDB.open(\"store\", *!*2*/!*);\n\nopenRequest.onupgradeneeded = function(event) {\n  // the existing database version is less than 2 (or it doesn't exist)\n  let db = openRequest.result;\n  switch(event.oldVersion) { // existing db version\n    case 0:\n      // version 0 means that the client had no database\n      // perform initialization\n    case 1:\n      // client had version 1\n      // update\n  }\n};\n```\n\nPlease note: as our current version is `2`, the `onupgradeneeded` handler has a code branch for version `0`, suitable for users that are accessing for the first time and have no database, and also for version `1`, for upgrades.\n\nAnd then, only if `onupgradeneeded` handler finishes without errors, `openRequest.onsuccess` triggers, and the database is considered successfully opened.\n\nTo delete a database:\n\n```js\nlet deleteRequest = indexedDB.deleteDatabase(name)\n// deleteRequest.onsuccess/onerror tracks the result\n```\n\n```warn header=\"We can't open an older version of the database\"\nIf the current user database has a higher version than in the `open` call, e.g. the existing DB version is `3`, and we try to `open(...2)`, then that's an error, `openRequest.onerror` triggers.\n\nThat's rare, but such a thing may happen when a visitor loads outdated JavaScript code, e.g. from a proxy cache. So the code is old, but his database is new.\n\nTo protect from errors, we should check `db.version` and suggest a page reload. Use proper HTTP caching headers to avoid loading the old code, so that you'll never have such problems.\n```\n\n### Parallel update problem\n\nAs we're talking about versioning, let's tackle a small related problem.\n\nLet's say:\n1. A visitor opened our site in a browser tab, with database version `1`.\n2. Then we rolled out an update, so our code is newer.\n3. And then the same visitor opens our site in another tab.\n\nSo there's a tab with an open connection to DB version `1`, while the second one attempts to update it to version `2` in its `upgradeneeded` handler.\n\nThe problem is that a database is shared between two tabs, as it's the same site, same origin. And it can't be both version `1` and `2`. To perform the update to version `2`, all connections to version 1 must be closed, including the one in the first tab.\n\nIn order to organize that, the `versionchange` event triggers on the \"outdated\" database object. We should listen for it and close the old database connection (and probably suggest a page reload, to load the updated code).\n\nIf we don't listen for the `versionchange` event and don't close the old connection, then the second, new connection won't be made. The `openRequest` object will emit the `blocked` event instead of `success`. So the second tab won't work.\n\nHere's the code to correctly handle the parallel upgrade. It installs the `onversionchange` handler, that triggers if the current database connection becomes outdated (db version is updated elsewhere) and closes the connection.\n\n```js\nlet openRequest = indexedDB.open(\"store\", 2);\n\nopenRequest.onupgradeneeded = ...;\nopenRequest.onerror = ...;\n\nopenRequest.onsuccess = function() {\n  let db = openRequest.result;\n\n  *!*\n  db.onversionchange = function() {\n    db.close();\n    alert(\"Database is outdated, please reload the page.\")\n  };\n  */!*\n\n  // ...the db is ready, use it...\n};\n\n*!*\nopenRequest.onblocked = function() {\n  // this event shouldn't trigger if we handle onversionchange correctly\n\n  // it means that there's another open connection to the same database\n  // and it wasn't closed after db.onversionchange triggered for it\n};\n*/!*\n```\n\n...In other words, here we do two things:\n\n1. The `db.onversionchange` listener informs us about a parallel update attempt, if the current database version becomes outdated.\n2. The `openRequest.onblocked` listener informs us about the opposite situation: there's a connection to an outdated version elsewhere, and it doesn't close, so the newer connection can't be made.\n\nWe can handle things more gracefully in `db.onversionchange`, prompt the visitor to save the data before the connection is closed and so on. \n\nOr, an alternative approach would be to not close the database in `db.onversionchange`, but instead use the `onblocked` handler (in the new tab) to alert the visitor, tell him that the newer version can't be loaded until they close other tabs.\n\nThese update collisions happen rarely, but we should at least have some handling for them, at least an `onblocked` handler, to prevent our script from dying silently.\n\n## Object store\n\nTo store something in IndexedDB, we need an *object store*.\n\nAn object store is a core concept of IndexedDB. Counterparts in other databases are called \"tables\" or \"collections\". It's where the data is stored. A database may have multiple stores: one for users, another one for goods, etc.\n\nDespite being named an \"object store\", primitives can be stored too.\n\n**We can store almost any value, including complex objects.**\n\nIndexedDB uses the [standard serialization algorithm](https://www.w3.org/TR/html53/infrastructure.html#section-structuredserializeforstorage) to clone-and-store an object. It's like `JSON.stringify`, but more powerful, capable of storing much more datatypes.\n\nAn example of an object that can't be stored: an object with circular references. Such objects are not serializable. `JSON.stringify` also fails for such objects.\n\n**There must be a unique `key` for every value in the store.**     \n\nA key must be one of these types - number, date, string, binary, or array. It's a unique identifier, so we can search/remove/update values by the key.\n\n![](indexeddb-structure.svg)\n\n\nAs we'll see very soon, we can provide a key when we add a value to the store, similar to `localStorage`. But when we store objects, IndexedDB allows setting up an object property as the key, which is much more convenient. Or we can auto-generate keys.\n\nBut we need to create an object store first.\n\n\nThe syntax to create an object store:\n```js\ndb.createObjectStore(name[, keyOptions]);\n```\n\nPlease note, the operation is synchronous, no `await` needed.\n\n- `name` is the store name, e.g. `\"books\"` for books,\n- `keyOptions` is an optional object with one of two properties:\n  - `keyPath` -- a path to an object property that IndexedDB will use as the key, e.g. `id`.\n  - `autoIncrement` -- if `true`, then the key for a newly stored object is generated automatically, as an ever-incrementing number.\n\nIf we don't supply `keyOptions`, then we'll need to provide a key explicitly later, when storing an object.\n\nFor instance, this object store uses `id` property as the key:\n```js\ndb.createObjectStore('books', {keyPath: 'id'});\n```\n\n**An object store can only be created/modified while updating the DB version, in `upgradeneeded` handler.**\n\nThat's a technical limitation. Outside of the handler we'll be able to add/remove/update the data, but object stores can only be created/removed/altered during a version update.\n\nTo perform a database version upgrade, there are two main approaches:\n1. We can implement per-version upgrade functions: from 1 to 2, from 2 to 3, from 3 to 4 etc. Then, in `upgradeneeded` we can compare versions (e.g. old 2, now 4) and run per-version upgrades step by step, for every intermediate version (2 to 3, then 3 to 4).\n2. Or we can just examine the database: get a list of existing object stores as `db.objectStoreNames`. That object is a [DOMStringList](https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#domstringlist) that provides `contains(name)` method to check for existance. And then we can do updates depending on what exists and what doesn't.\n\nFor small databases the second variant may be simpler.\n\nHere's the demo of the second approach:\n\n```js\nlet openRequest = indexedDB.open(\"db\", 2);\n\n// create/upgrade the database without version checks\nopenRequest.onupgradeneeded = function() {\n  let db = openRequest.result;\n  if (!db.objectStoreNames.contains('books')) { // if there's no \"books\" store\n    db.createObjectStore('books', {keyPath: 'id'}); // create it\n  }\n};\n```\n\n\nTo delete an object store:\n\n```js\ndb.deleteObjectStore('books')\n```\n\n## Transactions\n\nThe term \"transaction\" is generic, used in many kinds of databases.\n\nA transaction is a group of operations, that should either all succeed or all fail.\n\nFor instance, when a person buys something, we need to:\n1. Subtract the money from their account.\n2. Add the item to their inventory.\n\nIt would be pretty bad if we complete the 1st operation, and then something goes wrong, e.g. lights out, and we fail to do the 2nd. Both should either succeed (purchase complete, good!) or both fail (at least the person kept their money, so they can retry).\n\nTransactions can guarantee that.\n\n**All data operations must be made within a transaction in IndexedDB.**\n\nTo start a transaction:\n\n```js\ndb.transaction(store[, type]);\n```\n\n- `store` is a store name that the transaction is going to access, e.g. `\"books\"`. Can be an array of store names if we're going to access multiple stores.\n- `type` – a transaction type, one of:\n  - `readonly` -- can only read, the default.\n  - `readwrite` -- can only read and write the data, but not create/remove/alter object stores.\n\nThere's also `versionchange` transaction type: such transactions can do everything, but we can't create them manually. IndexedDB automatically creates a `versionchange` transaction when opening the database, for `updateneeded` handler. That's why it's a single place where we can update the database structure, create/remove object stores.\n\n```smart header=\"Why are there different types of transactions?\"\nPerformance is the reason why transactions need to be labeled either `readonly` and `readwrite`.\n\nMany `readonly` transactions are able to access the same store concurrently, but `readwrite` transactions can't. A `readwrite` transaction \"locks\" the store for writing. The next transaction must wait before the previous one finishes before accessing the same store.\n```\n\nAfter the transaction is created, we can add an item to the store, like this:\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\"); // (1)\n\n// get an object store to operate on it\n*!*\nlet books = transaction.objectStore(\"books\"); // (2)\n*/!*\n\nlet book = {\n  id: 'js',\n  price: 10,\n  created: new Date()\n};\n\n*!*\nlet request = books.add(book); // (3)\n*/!*\n\nrequest.onsuccess = function() { // (4)\n  console.log(\"Book added to the store\", request.result);\n};\n\nrequest.onerror = function() {\n  console.log(\"Error\", request.error);\n};\n```\n\nThere were basically four steps:\n\n1. Create a transaction, mentioning all the stores it's going to access, at `(1)`.\n2. Get the store object using `transaction.objectStore(name)`, at `(2)`.\n3. Perform the request to the object store `books.add(book)`, at `(3)`.\n4. ...Handle request success/error `(4)`, then we can make other requests if needed, etc.\n\nObject stores support two methods to store a value:\n\n- **put(value, [key])**\n    Add the `value` to the store. The `key` is supplied only if the object store did not have `keyPath` or `autoIncrement` option. If there's already a value with the same key, it will be replaced.\n\n- **add(value, [key])**\n    Same as `put`, but if there's already a value with the same key, then the request fails, and an error with the name `\"ConstraintError\"` is generated.\n\nSimilar to opening a database, we can send a request: `books.add(book)`, and then wait for `success/error` events.\n\n- The `request.result` for `add` is the key of the new object.\n- The error is in `request.error` (if any).\n\n## Transactions' autocommit\n\nIn the example above we started the transaction and made `add` request. But as we stated previously, a transaction may have multiple associated requests, that must either all succeed or all fail. How do we mark the transaction as finished, with no more requests to come?\n\nThe short answer is: we don't.\n\nIn the next version 3.0 of the specification, there will probably be a manual way to finish the transaction, but right now in 2.0 there isn't.\n\n**When all transaction requests are finished, and the [microtasks queue](info:microtask-queue) is empty, it is committed automatically.**\n\nUsually, we can assume that a transaction commits when all its requests are complete, and the current code finishes.\n\nSo, in the example above no special call is needed to finish the transaction.\n\nTransactions auto-commit principle has an important side effect. We can't insert an async operation like `fetch`, `setTimeout` in the middle of a transaction. IndexedDB will not keep the transaction waiting till these are done.\n\nIn the code below, `request2` in the line `(*)` fails, because the transaction is already committed, and can't make any request in it:\n\n```js\nlet request1 = books.add(book);\n\nrequest1.onsuccess = function() {\n  fetch('/').then(response => {\n*!*\n    let request2 = books.add(anotherBook); // (*)\n*/!*\n    request2.onerror = function() {\n      console.log(request2.error.name); // TransactionInactiveError\n    };\n  });\n};\n```\n\nThat's because `fetch` is an asynchronous operation, a macrotask. Transactions are closed before the browser starts doing macrotasks.\n\nAuthors of IndexedDB spec believe that transactions should be short-lived. Mostly for performance reasons.\n\nNotably, `readwrite` transactions \"lock\" the stores for writing. So if one part of the application initiated `readwrite` on `books` object store, then another part that wants to do the same has to wait: the new transaction \"hangs\" till the first one is done. That can lead to strange delays if transactions take a long time.\n\nSo, what to do?\n\nIn the example above we could make a new `db.transaction` right before the new request `(*)`.\n\nBut it will be even better, if we'd like to keep the operations together, in one transaction, to split apart IndexedDB transactions and \"other\" async stuff.\n\nFirst, make `fetch`, prepare the data if needed, afterwards create a transaction and perform all the database requests, it'll work then.\n\nTo detect the moment of successful completion, we can listen to `transaction.oncomplete` event:\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\");\n\n// ...perform operations...\n\ntransaction.oncomplete = function() {\n  console.log(\"Transaction is complete\");\n};\n```\n\nOnly `complete` guarantees that the transaction is saved as a whole. Individual requests may succeed, but the final write operation may go wrong (e.g. I/O error or something).\n\nTo manually abort the transaction, call:\n\n```js\ntransaction.abort();\n```\n\nThat cancels all modification made by the requests in it and triggers `transaction.onabort` event.\n\n\n## Error handling\n\nWrite requests may fail.\n\nThat's to be expected, not only because of possible errors at our side, but also for reasons not related to the transaction itself. For instance, the storage quota may be exceeded. So we must be ready to handle such case.\n\n**A failed request automatically aborts the transaction, canceling all its changes.**\n\nIn some situations, we may want to handle the failure (e.g. try another request), without canceling existing changes, and continue the transaction. That's possible. The `request.onerror` handler is able to prevent the transaction abort by calling `event.preventDefault()`.\n\nIn the example below a new book is added with the same key (`id`) as the existing one. The `store.add` method generates a `\"ConstraintError\"` in that case. We handle it without canceling the transaction:\n\n```js\nlet transaction = db.transaction(\"books\", \"readwrite\");\n\nlet book = { id: 'js', price: 10 };\n\nlet request = transaction.objectStore(\"books\").add(book);\n\nrequest.onerror = function(event) {\n  // ConstraintError occurs when an object with the same id already exists\n  if (request.error.name == \"ConstraintError\") {\n    console.log(\"Book with such id already exists\"); // handle the error\n    event.preventDefault(); // don't abort the transaction\n    // use another key for the book?\n  } else {\n    // unexpected error, can't handle it\n    // the transaction will abort\n  }\n};\n\ntransaction.onabort = function() {\n  console.log(\"Error\", transaction.error);\n};\n```\n\n### Event delegation\n\nDo we need onerror/onsuccess for every request? Not every time. We can use event delegation instead.\n\n**IndexedDB events bubble: `request` -> `transaction` -> `database`.**\n\nAll events are DOM events, with capturing and bubbling, but usually only bubbling stage is used.\n\nSo we can catch all errors using `db.onerror` handler, for reporting or other purposes:\n\n```js\ndb.onerror = function(event) {\n  let request = event.target; // the request that caused the error\n\n  console.log(\"Error\", request.error);\n};\n```\n\n...But what if an error is fully handled? We don't want to report it in that case.\n\nWe can stop the bubbling and hence `db.onerror` by using `event.stopPropagation()` in `request.onerror`.\n\n```js\nrequest.onerror = function(event) {\n  if (request.error.name == \"ConstraintError\") {\n    console.log(\"Book with such id already exists\"); // handle the error\n    event.preventDefault(); // don't abort the transaction\n    event.stopPropagation(); // don't bubble error up, \"chew\" it\n  } else {\n    // do nothing\n    // transaction will be aborted\n    // we can take care of error in transaction.onabort\n  }\n};\n```\n\n## Searching\n\nThere are two main types of search in an object store:\n\n1. By a key value or a key range. In our \"books\" storage that would be a value or range of values of `book.id`.\n2. By another object field, e.g. `book.price`. This required an additional data structure, named \"index\".\n\n### By key\n\nFirst let's deal with the first type of search: by key.\n\nSearching methods support both exact key values and so-called \"ranges of values\" -- [IDBKeyRange](https://www.w3.org/TR/IndexedDB/#keyrange) objects that specify an acceptable \"key range\".\n\n`IDBKeyRange` objects are created using following calls:\n\n- `IDBKeyRange.lowerBound(lower, [open])` means: `≥lower` (or `>lower` if `open` is true)\n- `IDBKeyRange.upperBound(upper, [open])` means: `≤upper` (or `<upper` if `open` is true)\n- `IDBKeyRange.bound(lower, upper, [lowerOpen], [upperOpen])` means: between `lower` and `upper`. If the open flags is true, the corresponding key is not included in the range.\n- `IDBKeyRange.only(key)` -- a range that consists of only one `key`, rarely used.\n\nWe'll see practical examples of using them very soon.\n\nTo perform the actual search, there are following methods. They accept a `query` argument that can be either an exact key or a key range:\n\n- `store.get(query)` -- search for the first value by a key or a range.\n- `store.getAll([query], [count])` -- search for all values, limit by `count` if given.\n- `store.getKey(query)` -- search for the first key that satisfies the query, usually a range.\n- `store.getAllKeys([query], [count])` -- search for all keys that satisfy the query, usually a range, up to `count` if given.\n- `store.count([query])` -- get the total count of keys that satisfy the query, usually a range.\n\nFor instance, we have a lot of books in our store. Remember, the `id` field is the key, so all these methods can search by `id`.\n\nRequest examples:\n\n```js\n// get one book\nbooks.get('js')\n\n// get books with 'css' <= id <= 'html'\nbooks.getAll(IDBKeyRange.bound('css', 'html'))\n\n// get books with id < 'html'\nbooks.getAll(IDBKeyRange.upperBound('html', true))\n\n// get all books\nbooks.getAll()\n\n// get all keys, where id > 'js'\nbooks.getAllKeys(IDBKeyRange.lowerBound('js', true))\n```\n\n```smart header=\"Object store is always sorted\"\nAn object store sorts values by key internally.\n\nSo requests that return many values always return them in sorted by key order.\n```\n\n### By a field using an index\n\nTo search by other object fields, we need to create an additional data structure named \"index\".\n\nAn index is an \"add-on\" to the store that tracks a given object field. For each value of that field, it stores a list of keys for objects that have that value. There will be a more detailed picture below.\n\nThe syntax:\n\n```js\nobjectStore.createIndex(name, keyPath, [options]);\n```\n\n- **`name`** -- index name,\n- **`keyPath`** -- path to the object field that the index should track (we're going to search by that field),\n- **`option`** -- an optional object with properties:\n  - **`unique`** -- if true, then there may be only one object in the store with the given value at the `keyPath`. The index will enforce that by generating an error if we try to add a duplicate.\n  - **`multiEntry`** -- only used if the value on `keyPath` is an array. In that case, by default, the index will treat the whole array as the key. But if `multiEntry` is true, then the index will keep a list of store objects for each value in that array. So array members become index keys.\n\nIn our example, we store books keyed by `id`.\n\nLet's say we want to search by `price`.\n\nFirst, we need to create an index. It must be done in `upgradeneeded`, just like an object store:\n\n```js\nopenRequest.onupgradeneeded = function() {\n  // we must create the index here, in versionchange transaction\n  let books = db.createObjectStore('books', {keyPath: 'id'});\n*!*\n  let index = books.createIndex('price_idx', 'price');\n*/!*\n};\n```\n\n- The index will track `price` field.\n- The price is not unique, there may be multiple books with the same price, so we don't set `unique` option.\n- The price is not an array, so `multiEntry` flag is not applicable.\n\nImagine that our `inventory` has 4 books. Here's the picture that shows exactly what the `index` is:\n\n![](indexeddb-index.svg)\n\nAs said, the index for each value of `price` (second argument) keeps the list of keys that have that price.\n\nThe index keeps itself up to date automatically, we don't have to care about it.\n\nNow, when we want to search for a given price, we simply apply the same search methods to the index:\n\n```js\nlet transaction = db.transaction(\"books\"); // readonly\nlet books = transaction.objectStore(\"books\");\nlet priceIndex = books.index(\"price_idx\");\n\n*!*\nlet request = priceIndex.getAll(10);\n*/!*\n\nrequest.onsuccess = function() {\n  if (request.result !== undefined) {\n    console.log(\"Books\", request.result); // array of books with price=10\n  } else {\n    console.log(\"No such books\");\n  }\n};\n```\n\nWe can also use `IDBKeyRange` to create ranges and looks for cheap/expensive books:\n\n```js\n// find books where price <= 5\nlet request = priceIndex.getAll(IDBKeyRange.upperBound(5));\n```\n\nIndexes are internally sorted by the tracked object field, `price` in our case. So when we do the search, the results are also sorted by `price`.\n\n## Deleting from store\n\nThe `delete` method looks up values to delete by a query, the call format is similar to `getAll`:\n\n- **`delete(query)`** -- delete matching values by query.\n\nFor instance:\n```js\n// delete the book with id='js'\nbooks.delete('js');\n```\n\nIf we'd like to delete books based on a price or another object field, then we should first find the key in the index, and then call `delete`:\n\n```js\n// find the key where price = 5\nlet request = priceIndex.getKey(5);\n\nrequest.onsuccess = function() {\n  let id = request.result;\n  let deleteRequest = books.delete(id);\n};\n```\n\nTo delete everything:\n```js\nbooks.clear(); // clear the storage.\n```\n\n## Cursors\n\nMethods like `getAll/getAllKeys` return an array of keys/values.\n\nBut an object storage can be huge, bigger than the available memory. Then `getAll` will fail to get all records as an array.\n\nWhat to do?\n\nCursors provide the means to work around that.\n\n**A *cursor* is a special object that traverses the object storage, given a query, and returns one key/value at a time, thus saving memory.**\n\nAs an object store is sorted internally by key, a cursor walks the store in key order (ascending by default).\n\nThe syntax:\n```js\n// like getAll, but with a cursor:\nlet request = store.openCursor(query, [direction]);\n\n// to get keys, not values (like getAllKeys): store.openKeyCursor\n```\n\n- **`query`** is a key or a key range, same as for `getAll`.\n- **`direction`** is an optional argument, which order to use:\n  - `\"next\"` -- the default, the cursor walks up from the record with the lowest key.\n  - `\"prev\"` -- the reverse order: down from the record with the biggest key.\n  - `\"nextunique\"`, `\"prevunique\"` -- same as above, but skip records with the same key (only for cursors over indexes, e.g. for multiple books with price=5 only the first one will be returned).\n\n**The main difference of the cursor is that `request.onsuccess` triggers multiple times: once for each result.**\n\nHere's an example of how to use a cursor:\n\n```js\nlet transaction = db.transaction(\"books\");\nlet books = transaction.objectStore(\"books\");\n\nlet request = books.openCursor();\n\n// called for each book found by the cursor\nrequest.onsuccess = function() {\n  let cursor = request.result;\n  if (cursor) {\n    let key = cursor.key; // book key (id field)\n    let value = cursor.value; // book object\n    console.log(key, value);\n    cursor.continue();\n  } else {\n    console.log(\"No more books\");\n  }\n};\n```\n\nThe main cursor methods are:\n\n- `advance(count)` -- advance the cursor `count` times, skipping values.\n- `continue([key])` -- advance the cursor to the next value in range matching (or immediately after `key` if given).\n\nWhether there are more values matching the cursor or not -- `onsuccess` gets called, and then in `result` we can get the cursor pointing to the next record, or `undefined`.\n\nIn the example above the cursor was made for the object store.\n\nBut we also can make a cursor over an index. As we remember, indexes allow to search by an object field. Cursors over indexes do precisely the same as over object stores -- they save memory by returning one value at a time.\n\nFor cursors over indexes, `cursor.key` is the index key (e.g. price), and we should use `cursor.primaryKey` property for the object key:\n\n```js\nlet request = priceIdx.openCursor(IDBKeyRange.upperBound(5));\n\n// called for each record\nrequest.onsuccess = function() {\n  let cursor = request.result;\n  if (cursor) {\n    let primaryKey = cursor.primaryKey; // next object store key (id field)\n    let value = cursor.value; // next object store object (book object)\n    let key = cursor.key; // next index key (price)\n    console.log(key, value);\n    cursor.continue();\n  } else {\n    console.log(\"No more books\");\n  }\n};\n```\n\n## Promise wrapper\n\nAdding `onsuccess/onerror` to every request is quite a cumbersome task. Sometimes we can make our life easier by using event delegation, e.g. set handlers on the whole transactions, but `async/await` is much more convenient.\n\nLet's use a thin promise wrapper <https://github.com/jakearchibald/idb> further in this chapter. It creates a global `idb` object with [promisified](info:promisify) IndexedDB methods.\n\nThen, instead of `onsuccess/onerror` we can write like this:\n\n```js\nlet db = await idb.openDB('store', 1, db => {\n  if (db.oldVersion == 0) {\n    // perform the initialization\n    db.createObjectStore('books', {keyPath: 'id'});\n  }\n});\n\nlet transaction = db.transaction('books', 'readwrite');\nlet books = transaction.objectStore('books');\n\ntry {\n  await books.add(...);\n  await books.add(...);\n\n  await transaction.complete;\n\n  console.log('jsbook saved');\n} catch(err) {\n  console.log('error', err.message);\n}\n\n```\n\nSo we have all the sweet \"plain async code\" and \"try..catch\" stuff.\n\n### Error handling\n\nIf we don't catch an error, then it falls through, till the closest outer `try..catch`.\n\nAn uncaught error becomes an \"unhandled promise rejection\" event on `window` object.\n\nWe can handle such errors like this:\n\n```js\nwindow.addEventListener('unhandledrejection', event => {\n  let request = event.target; // IndexedDB native request object\n  let error = event.reason; //  Unhandled error object, same as request.error\n  ...report about the error...\n});\n```\n\n### \"Inactive transaction\" pitfall\n\n\nAs we already know, a transaction auto-commits as soon as the browser is done with the current code and microtasks. So if we put a *macrotask* like `fetch` in the middle of a transaction, then the transaction won't wait for it to finish. It just auto-commits. So the next request in it would fail.\n\n\nFor a promise wrapper and `async/await` the situation is the same.\n\nHere's an example of `fetch` in the middle of the transaction:\n\n```js\nlet transaction = db.transaction(\"inventory\", \"readwrite\");\nlet inventory = transaction.objectStore(\"inventory\");\n\nawait inventory.add({ id: 'js', price: 10, created: new Date() });\n\nawait fetch(...); // (*)\n\nawait inventory.add({ id: 'js', price: 10, created: new Date() }); // Error\n```\n\nThe next `inventory.add` after `fetch` `(*)` fails with an \"inactive transaction\" error, because the transaction is already committed and closed at that time.\n\nThe workaround is the same as when working with native IndexedDB: either make a new transaction or just split things apart.\n1. Prepare the data and fetch all that's needed first.\n2. Then save in the database.\n\n### Getting native objects\n\nInternally, the wrapper performs a native IndexedDB request, adding `onerror/onsuccess` to it, and returns a promise that rejects/resolves with the result.\n\nThat works fine most of the time. The examples are at the lib page <https://github.com/jakearchibald/idb>.\n\nIn few rare cases, when we need the original `request` object, we can access it as `promise.request` property of the promise:\n\n```js\nlet promise = books.add(book); // get a promise (don't await for its result)\n\nlet request = promise.request; // native request object\nlet transaction = request.transaction; // native transaction object\n\n// ...do some native IndexedDB voodoo...\n\nlet result = await promise; // if still needed\n```\n\n## Summary\n\nIndexedDB can be thought of as a \"localStorage on steroids\". It's a simple key-value database, powerful enough for offline apps, yet simple to use.\n\nThe best manual is the specification, [the current one](https://www.w3.org/TR/IndexedDB-2/) is 2.0, but few methods from [3.0](https://w3c.github.io/IndexedDB/) (it's not much different) are partially supported.\n\nThe basic usage can be described with a few phrases:\n\n1. Get a promise wrapper like [idb](https://github.com/jakearchibald/idb).\n2. Open a database: `idb.openDb(name, version, onupgradeneeded)`\n    - Create object storages and indexes in `onupgradeneeded` handler or perform version update if needed.\n3. For requests:\n    - Create transaction `db.transaction('books')` (readwrite if needed).\n    - Get the object store `transaction.objectStore('books')`.\n4. Then, to search by a key, call methods on the object store directly.\n    - To search by an object field, create an index.\n5. If the data does not fit in memory, use a cursor.\n\nHere's a small demo app:\n\n[codetabs src=\"books\" current=\"index.html\"]\n"
  },
  {
    "path": "6-data-storage/03-indexeddb/books.view/index.html",
    "content": "<!doctype html>\n<script src=\"https://cdn.jsdelivr.net/npm/idb@3.0.2/build/idb.min.js\"></script>\n\n<button onclick=\"addBook()\">Add a book</button>\n<button onclick=\"clearBooks()\">Clear books</button>\n\n<p>Books list:</p>\n\n<ul id=\"listElem\"></ul>\n\n<script>\nlet db;\n\ninit();\n\nasync function init() {\n  db = await idb.openDb('booksDb', 1, db => {\n    db.createObjectStore('books', {keyPath: 'name'});\n  });\n\n  list();\n}\n\nasync function list() {\n  let tx = db.transaction('books');\n  let bookStore = tx.objectStore('books');\n\n  let books = await bookStore.getAll();\n\n  if (books.length) {\n    listElem.innerHTML = books.map(book => `<li>\n        name: ${book.name}, price: ${book.price}\n      </li>`).join('');\n  } else {\n    listElem.innerHTML = '<li>No books yet. Please add books.</li>'\n  }\n\n\n}\n\nasync function clearBooks() {\n  let tx = db.transaction('books', 'readwrite');\n  await tx.objectStore('books').clear();\n  await list();\n}\n\nasync function addBook() {\n  let name = prompt(\"Book name?\");\n  let price = +prompt(\"Book price?\");\n\n  let tx = db.transaction('books', 'readwrite');\n\n  try {\n    await tx.objectStore('books').add({name, price});\n    await list();\n  } catch(err) {\n    if (err.name == 'ConstraintError') {\n      alert(\"Such book exists already\");\n      await addBook();\n    } else {\n      throw err;\n    }\n  }\n}\n\nwindow.addEventListener('unhandledrejection', event => {\n  alert(\"Error: \" + event.reason.message);\n});\n\n</script>\n"
  },
  {
    "path": "6-data-storage/index.md",
    "content": "# Menyimpan data didalam _Browser_\n"
  },
  {
    "path": "7-animation/1-bezier-curve/article.md",
    "content": "# _Bezier curve_\n\n_Bezier Curves_ digunakan didalam grafis komputer untuk menggambar bentuk, untuk animasi _CSS_ dan di bagian lainnya.\n\n_Bezier Curves_ merupakan hal yang cukup simple, dan sangat layak untuk dipelajari dan akan terasa kenyamanan penggunaannya didalam grafis vektor dan animasi lanjutan.\n\n## Titik Kontrol\n\nSebuah [bezier curve](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) didefinisikan dengan menggunakan titik kontrol.\n\nKemungkinan akan terdapat 2, 3, 4 atau lebih.\n\nFor instance, two points curve:\nUntuk contoh ini menggunakan dua titik lengkungan.\n\n![](bezier2.svg)\n\nTiga titik lengkungan:\n\n![](bezier3.svg)\n\nEmpat titik lengkungan:\n\n![](bezier4.svg)\n\nJika kamu perhatikan secara seksama, kamu dapat melihat:\n\n1. **Titik tidak selalu sama dengan lengkungan.** Itu merupakan hal yang normal, kita akan lihat nanti bagaimana lengkungan itu diciptakan.\n2. **Urutan lengkungan sama dengan titik dikurangi satu.**\n   Untuk dua titik kita mempunyai lengkungan linear (garis lurus), untuk tiga titik kita mempunyai -- garis kuadratik (parabolik), untuk empat titik -- _cubic curve_ atau lengkungan kubik.\n3. **Sebuah lengkungan selalu didalam [convex hull](https://en.wikipedia.org/wiki/Convex_hull) dari titik kontrol.**\n\n   ![](bezier4-e.svg) ![](bezier3-e.svg)\n\nKarena properti terakhir, didalam komputer grafis memungkinkan untuk mengoptimalkan test titik potong. Jika _Convex Hull_ tidak berpotongan maka lengkungannya juga tidak akan berpotongan. Jadi memeriksa potongan _convex hull_ di awal akan memberikan hasil \"tanpa potongan\" dengan cepat. Memeriksa potongan _convex hull_ lebih mudah dilakukan karena bentuknya kotak, segitiga dan lainnya (lihat contoh diatas), bentuk yang lebih simple daripada lengkungan.\n\n**Nilai utaman dari _Bezier Curves_ untuk menggambar -- dengan memindahkan titik lengkungan dengan cara yang jelas secara intuitif.**\n\nCobalah memindahkan titik kontrol menggunakan _mouse_ di contoh dibawah:\n\n[iframe src=\"demo.svg?nocpath=1&p=0,0,0.5,0,0.5,1,1,1\" height=370]\n\n**Seperti yang kamu perhatikan, lengkungannya meregang bersama dengan garis tangen 1 -> 2 dan 3 -> 4.**\n\nSetelah beberapa latihan nantinya akan jelas bagaimana caranya untuk memasangkan titik agar menjari lengkungan. dan menyambungkan beberapa lengkungan kita dapat membuat apapun.\n\nIni adalah beberapa contoh:\n\n![](bezier-car.svg) ![](bezier-letter.svg) ![](bezier-vase.svg)\n\n## Algoritma _De Casteljau's_\n\nTerdapat sebuah rumus matematika untuk _Bezier Curve_, tapi kita akan membahasnya nanti karena [De Casteljau's algorithm](https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm) identik dengan definisinya sendiri dan dapat dilihat jelas bagaimana membangunnya.\n\nPertama-tama kita lihat contoh 3 titik.\n\nIni adalah contoh dan penjelasannya akan mengikuti.\n\nTitik kontrol (1, 2 dan 3) bisa dipindahkan dengan _mouse_. Tekan _play_ untuk menjalankan.\n\n[iframe src=\"demo.svg?p=0,0,0.5,1,1,0&animate=1\" height=370]\n\n**Algoritma _De Casteljau's_ dengan 3 titik _bezier curve_**\n\n1. Letakan titik kontrol. Di dalam contoh diatas dinamai dengan: `1`, `2`, `3`.\n2. Buat bagian diantara titik kontrol 1 -> 2 -> 3. Didalam contoh diatas adalah <span style=\"color:#825E28\">coklat</span>.\n3. Parameter `t` bergerak dari `0` menuju `1`. Didalam contoh diatas langkah `0.05` digunakan: perulangannya bergerak menuju `0, 0.05, 0.1, 0.15, ... 0.95, 1`.\n\n   Untuk setiap nilai dari `t`:\n\n   - Untuk setiap bagian <span style=\"color:#825E28\">coklat</span> kita bisa mengambil titik lokasi yang cocok untuk `t` dari titik mulainya. Karena terdapat dua bagian, kita memiliki dua titik.\n\n     Untuk contoh, untuk `t=0` -- untuk kedua titik akan berada pada awal bagian, dan untuk `t=0.25` -- pada 25% dari panjang bagian dari awal, untuk `t=0.5` -- 50%(ditengah), untuk `t=1` `` diakhir dari bagian.\n\n   - Sambungkan titiknya. Didalam gambar dibawah bagian yang tersambung di warnai<span style=\"color:#167490\">biru</span>.\n\n| Untuk `t=0.25`         | Untuk `t=0.5`          |\n| ---------------------- | ---------------------- |\n| ![](bezier3-draw1.svg) | ![](bezier3-draw2.svg) |\n\n4. Sekarang pada bagian <span style=\"color:#167490\">biru</span> ambil titik yang proporsional yang sama dengan `t`. Jadi, untuk `t=0.25` (gambar kiri) kita mempunyai titik pada bagian akhir dari seperempat bagian, dan untuk `t=0.5` (gambar kanan) -- di bagian tengah. Pada gambar atas titik itu berada pada <span style=\"color:red\">merah</span>.\n\n5. Sebagaimana `t` bergerak dari `0` ke `1`, setiap nilai dari `t` menambahkan titik pada lengkungannya. Satu kesatuan itu membentuk _Bezier Curve_. Yang berwarna merah dan parabolik pada gambar diatas.\n\nItu adalah proses dari 3 titik. Tapi sama dengan proses yang menggunakan 4 titik.\n\nContoh untuk 4 titik (titik dapat dipindahkan menggunakan _mouse_):\n\n[iframe src=\"demo.svg?p=0,0,0.5,0,0.5,1,1,1&animate=1\" height=370]\n\nAlgoritma untuk 4 titik:\n\n- Sambungkan titik kontrol pada bagian: 1 -> 2, 2 -> 3, 3 -> 4. Akan terdapat 3 bagian <span style=\"color:#825E28\">coklat</span>.\n- Untuk setiap `t` didalam interval dari `0` menuju `1`:\n  - Kita ambil titik dari bagian yang memiliki cukup jarak dengan `t` di awal. Titik ini tersambung, jadi kita mempunyai dua <span style=\"color:#0A0\">bagian hijau</span>.\n  - Salah satu dari bagiannya akan mempunyai jarak yang cukup ke `t`. Kita memiliki <span style=\"color:#167490\">bagian biru</span>.\n  - Di bagian biru kita mengambil sebuah titik yang memiliki jarak proporsional ke `t`. Di contoh diatas adalah <span style=\"color:red\">merah</span>.\n- Titik-titik ini akan membentuk lengkungan.\n\nAlgoritmanya adalah perulangan dan bisa digenerelasikan dengan jumlah titik berapapun itu.\n\nDiberikan titik kontrol dengan jumlah N:\n\n1. Kita sambungkan titik-titik nya untuk mendapatkan bagian awal N-1.\n2. Lalu untuk setiap `t` dari `0` sampai `1`, kita bisa mengambil titik dari setiap bagian. Akan terdapat bagian N -2.\n3. Ulangi langkah 2 sampai hanya tersisa satu titik.\n\nTitik-titik ini membangun lengkungannya.\n\n```online\n**Jalankan dan hentikan sementara untuk melihat bagian dan lengkungannya dibuat**\n```\n\nSebuah lengkungan yang terlihat seperti `y=1/t`:\n\n[iframe src=\"demo.svg?p=0,0,0,0.75,0.25,1,1,1&animate=1\" height=370]\n\nKontrol _zig-zag_ pun dapat digunakan:\n\n[iframe src=\"demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1\" height=370]\n\nMembuat perulangan juga bisa:\n\n[iframe src=\"demo.svg?p=0,0,1,0.5,0,1,0.5,0&animate=1\" height=370]\n\n_Bezier Curve_ yang tidak rapih (bisa dibuat juga):\n\n[iframe src=\"demo.svg?p=0,0,1,1,0,1,1,0&animate=1\" height=370]\n\n```online\nJika ada sesuatu yang tidak jelas dengan deskripsi algoritmanya, perhatikan contoh yang tersedia untuk mengetahui bagaimana lengkungannya dibuat.\n```\n\nSebagaimana algoritmanya adalah rekursif(berulang), kita bisa membangun _Bezier Curves_ dengan urutan apapun, maka dari itu: gunakan 5, 6 atau lebih titik kontrol. Tapi pada penggunaannya terlalu banyak titik tidak akan benar-benar berguna. Biasanya kita akan menggunakan 2-3 titik, dan untuk garis yang lebih rumit kita hanya akan menempelkan beberapa lengkungan yang berbeda. Hal itu akan membuat pekerjaan menjadi lebih mudah dilakukan dan dihitung.\n\n```smart header=\"Untuk menspesifikasikan _Bezier Curve_, digunakan titik kontrol. Seperti yang bisa kita lihat, titiknya tidak berapa pada lengkungannya, kecuali yang pertama dan terakhir.\"\n\nTerkadang kita memiliki hal lain: untuk menggambar lengkungan *melewati beberapa titik*, jadi semuanya digambar didalam satu lengkungan. hal tersebut dipanggil dengan [interpolation](https://en.wikipedia.org/wiki/Interpolation),dan disini kita tidak akan mempelajarinya.\n\nTerdapat beberapa rumus matematika seperti lengkungan, untuk contoh [Lagrange polynomial](https://en.wikipedia.org/wiki/Lagrange_polynomial). Didalam grafis komputer [spline interpolation](https://en.wikipedia.org/wiki/Spline_interpolation) sering digunakan untuk membangun lengkungan yang halus dengan menyambungkan beberapa titik.\n```\n\n## Matematikal\n\nSebuah _Bezier Curve_ bisa di buat dengan menggunakan rumus matematika.\n\nSeperti yang kita lihat -- sebenarnya tidak perlu kita ketahui, kebanyakan orang membangunnya dengan menggerakan titik menggunakan _mouse_. Tapi jika kamu menyukai matematika, beginilah caranya.\n\nDiberikan koordinat dari titik kontrol <code>P<sub>i</sub></code>: titik kontrol pertama memiliki koordinat <code>P<sub>1</sub> = (x<sub>1</sub>, y<sub>1</sub>)</code>, yang kedua <code>P<sub>2</sub> = (x<sub>2</sub>, y<sub>2</sub>)</code>, dan seterusnya, koordinat lengkungannya dejelaskan dengan persamaan yang bergantung pada parameter `t` dari bagian `[0.1]`.\n\n- Rumus untuk lengkungan dengan 2 titik:\n\n  <code>P = (1-t)P<sub>1</sub> + tP<sub>2</sub></code>\n\n- Untuk 3 titik:\n\n  <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>\n\n- Untuk 4 titik:\n\n  <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>\n\nIni adalah persamaan vektor. Dengan kata lain, kita bisa menyimpan `x` dan `y` daripada `P` untuk mendapatkan koordinat tertentu.\n\nUntuk contoh, lengkungan dengan 3 titik dibangun dengan titik `(x, y)` dihitung dengan:\n\n- <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>\n- <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>\n\nDaripada <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> kita harus memasukan koordinat dari 3 titik kontrol dan lalu selama `t` bergerak dari `0` menuju `1`, untuk setiap nilai dari `t` kita harus memiliki `(x, y)` dari lengkungannya.\n\nContoh, jika titik kontrol adalah `(0,0)`, `(0,5, 1)` dan `(1, 0)`, persamaannya menjadi:\n\n- <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>\n- <code>y = (1−t)<sup>2</sup> _ 0 + 2(1−t)t _ 1 + t<sup>2</sup> \\* 0 = 2(1-t)t = –2t<sup>2</sup> + 2t</code>\n\nSekarang selama `t` berjalan dari `0` menuju `1`, nilai dari `(x, y)` untuk setiap `t` membentuk lengkungan untuk titik kontrolnya.\n\n## Ringkasan\n\n_Bezier Curves_ didefinisikan menggunakan titik kontrol.\n\nKita melihat dua definisi dari _Bezier Curves_:\n\n1. Menggunakan proses gambar: _Algoritma De Casteljau's_.\n2. Menggunakan rumus matematika.\n\nProperti yang bagus dari _Bezier Curve_:\n\nKita bisa membuat garis halus dengan sebuah _mouse_ dengan menggerakan titik.\n\n- Bentuk yang rumit bisa dibuat dengan beberapa _Bezier Curve_.\n\nPenggunaan:\n\n- Didalam grafis komputer, permodelan, vektor dan editor grafis. _Fonts_ di kategorikan sebagai _Bezier Curves_.\n  -Di dalam pembangunan website -- untuk grafis didalam kanvas dan didalam format SVG. Untuk informasi, contoh yang ada diatas menggunakan format SVG. Contoh-contoh itu menggunakan satu dokumen SVG yang diberikan beberapa titik sebagai parameter. Kamu bisa melihat _source_ nya di [demo.svg](demo.svg?p=0,0,1,0.5,0,0.5,1,1&animate=1).\n- Dalam animasi CSS untuk menjelaskan _path_ dan kecepatan dari animasi.\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/solution.md",
    "content": "\nCSS untuk menganimasikan `width` dan `height`:\n```css\n/* class original */\n\n#flyjet {\n  transition: all 3s;\n}\n\n/* JS menambahkan .growing */\n#flyjet.growing {\n  width: 400px;\n  height: 240px;\n}\n```\n\nPerlu diingat bahwa `transitionend` memicu dua kali -- sekali untuk setiap properti. Jadi jika kita tidak ingin melakukan pengecekan tambahan maka pesannya akan muncul 2 kali."
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      height: 24px;\n      transition: all 3s;\n    }\n\n    #flyjet.growing {\n      width: 400px;\n      height: 240px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n  <script>\n    flyjet.onclick = function() {\n\n      let ended = false;\n\n      flyjet.addEventListener('transitionend', function() {\n        if (!ended) {\n          ended = true;\n          alert('Done!');\n        }\n      });\n\n      flyjet.classList.add('growing');\n    }\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      /* -> 400px */\n\n      height: 24px;\n      /* -> 240px */\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/1-animate-logo-css/task.md",
    "content": "Nilai: 5\n\n---\n\n# Animasi sebuah bidang (CSS)\n\nTampilkan animasi seperti gambar dibawah (klik pada bidang):\n\n[iframe src=\"solution\" height=300]\n\n- Gambarnya membesar saat di klik dari `40x24px` menjadi `400x240px` (10 kali lebih besar).\n- Animasinya memakan waktu 3 detik.\n- Saat selesai keluarkan: \"Done!\".\n- During the animation process, there may be more clicks on the plane. They shouldn't \"break\" anything.\n- Pada saat proses animasi, mungkin akan terjadi lebih dari 1 klik pada bidangnya. Klik-klik tersebut tidak boleh membuat aplikasinya *error*.\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/solution.md",
    "content": "Kita harus memilih *Bezier Curve* yang cocok untuk animasinya. Yang mana harus memiliki `y>1` agar bidangnya dapat \"melompat keluar\".\n\nContoh, kita bisa menggunakan titik kontrol dengan `y>1`, seperti `cubic-bezier(0.25, 1.5, 0.75, 1.5)`.\n\nGrafiknya:\n\n![](bezier-up.svg)\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    img {\n      display: block;\n      cursor: pointer;\n    }\n  </style>\n  <style>\n    #flyjet {\n      width: 40px;\n      height: 24px;\n      transition: all 3s cubic-bezier(0.25, 1.5, 0.75, 1.5);\n    }\n\n    #flyjet.growing {\n      width: 400px;\n      height: 240px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"flyjet\" src=\"https://en.js.cx/clipart/flyjet.jpg\">\n\n  <script>\n    flyjet.onclick = function() {\n      flyjet.classList.add('growing');\n    };\n  </script>\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/2-animate-logo-bezier-css/task.md",
    "content": "Nilai: 5\n\n---\n\n# Animasikan bidang yang terbang (CSS)\n\nModifikasi solusi dari tugas sebelumnya <info:task/animate-logo-css> untuk membuat bidang yang tumbuh dari ukuran aslinya 400x240px (melompat keluar)\n, dan kembalikan ukuran tersebut.\n\nIni adalah contoh bagaimana bidangnya terlihat (klik pada bidangnya):\n\n[iframe src=\"solution\" height=350]\n\nGunakan solusi dari tugas sebelumnya sebagai acuan.\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/solution.md",
    "content": ""
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .circle {\n      transition-property: width, height, margin-left, margin-top;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"showCircle(150, 150, 100)\">showCircle(150, 150, 100)</button>\n\n  <script>\n  function showCircle(cx, cy, radius) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    setTimeout(() => {\n      div.style.width = radius * 2 + 'px';\n      div.style.height = radius * 2 + 'px';\n    }, 0);\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/source.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .circle {\n      transition-property: width, height, margin-left, margin-top;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n\n      width: 200px;\n      height: 200px;\n      top: 150px;\n      left: 150px;\n    }\n  </style>\n</head>\n\n<body>\n\n  <div class=\"circle\"></div>\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/3-animate-circle/task.md",
    "content": "Nilai: 5\n\n---\n\n# Animasikan Lingkaran\n\nBuat sebuah fungsi `showCircle(cx, cy, radius)` yang menampilkan animasi lingkaran yang membesar.\n\n- `cx, cy` adalah koordinat *window-relative* dari tengah lingkaran,\n- `radius` adalah radius dari lingkarannya.\n\nKlik pada tombol dibawah untuk melihat contoh:\n\n[iframe src=\"solution\" height=260]\n\nDokumen utamanya memiliki contoh dari lingkaran dengan *styles* yang benar, jadi tugasnya adalah untuk membuat animasinya benar.\n"
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/solution.md",
    "content": ""
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/solution.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    .message-ball {\n      font-size: 20px;\n      line-height: 200px;\n      text-align: center;\n    }\n    .circle {\n      transition-property: width, height, margin-left, margin-top;\n      transition-duration: 2s;\n      position: fixed;\n      transform: translateX(-50%) translateY(-50%);\n      background-color: red;\n      border-radius: 50%;\n    }\n  </style>\n</head>\n\n<body>\n\n  <button onclick=\"go()\">Click me</button>\n\n  <script>\n\n  function go() {\n    showCircle(150, 150, 100, div => {\n      div.classList.add('message-ball');\n      div.append(\"Hello, world!\");\n    });\n  }\n\n  function showCircle(cx, cy, radius, callback) {\n    let div = document.createElement('div');\n    div.style.width = 0;\n    div.style.height = 0;\n    div.style.left = cx + 'px';\n    div.style.top = cy + 'px';\n    div.className = 'circle';\n    document.body.append(div);\n\n    setTimeout(() => {\n      div.style.width = radius * 2 + 'px';\n      div.style.height = radius * 2 + 'px';\n\n      div.addEventListener('transitionend', function handler() {\n        div.removeEventListener('transitionend', handler);\n        callback(div);\n      });\n    }, 0);\n  }\n  </script>\n\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/4-animate-circle-callback/task.md",
    "content": "\n# Lingkaran animasi dengan callback\n\nDalam tugas <info:task/animate-circle> sebuah lingkaran animasi di tampilkan.\n\nSekarang katakan kalau kita tidak hanya butuh lingkaran saja, tetapi juga butuh menampilkan pesan didalamnya. Pesan tersebut harus muncul *setelah* animasi nya selesai (lingkaran nya bergerak secara penuh), kalau tidak itu akan terlihat tidak bagus.\n\nDalam solusi tugas, fungsi `showCircle(cx, cy, radius)` menggambar lingkaran, tetapi tidak memberikan cara untuk melacak-nya ketika sudah selesai.\n\nTambahkan sebuah argumen callback: `showCircle(cx, cy, radius, callback)` untuk di panggil ketika animasi-nya sudah selesai. `callback` harusnya menerima `<div>` lingkaran sebagain argumen.\n\nSeperti ini contohnya:\n\n```js\nshowCircle(150, 150, 100, div => {\n  div.classList.add('message-ball');\n  div.append(\"Hello, world!\");\n});\n```\n\nDemo:\n\n[iframe src=\"solution\" height=260]\n\nAmbil solusinya untuk tugas <info:task/animate-circle> sebagai dasar.\n"
  },
  {
    "path": "7-animation/2-css-animations/article.md",
    "content": "# CSS-animations\n\nCSS animations make it possible to do simple animations without JavaScript at all.\n\nJavaScript can be used to control CSS animations and make them even better, with little code.\n\n## CSS transitions [#css-transition]\n\nThe idea of CSS transitions is simple. We describe a property and how its changes should be animated. When the property changes, the browser paints the animation.\n\nThat is, all we need is to change the property, and the fluid transition will be done by the browser.\n\nFor instance, the CSS below animates changes of `background-color` for 3 seconds:\n\n```css\n.animated {\n  transition-property: background-color;\n  transition-duration: 3s;\n}\n```\n\nNow if an element has `.animated` class, any change of `background-color` is animated during 3 seconds.\n\nClick the button below to animate the background:\n\n```html run autorun height=60\n<button id=\"color\">Click me</button>\n\n<style>\n  #color {\n    transition-property: background-color;\n    transition-duration: 3s;\n  }\n</style>\n\n<script>\n  color.onclick = function() {\n    this.style.backgroundColor = 'red';\n  };\n</script>\n```\n\nThere are 4 properties to describe CSS transitions:\n\n- `transition-property`\n- `transition-duration`\n- `transition-timing-function`\n- `transition-delay`\n\nWe'll cover them in a moment, for now let's note that the common `transition` property allows declaring them together in the order: `property duration timing-function delay`, as well as animating multiple properties at once.\n\nFor instance, this button animates both `color` and `font-size`:\n\n```html run height=80 autorun no-beautify\n<button id=\"growing\">Click me</button>\n\n<style>\n#growing {\n*!*\n  transition: font-size 3s, color 2s;\n*/!*\n}\n</style>\n\n<script>\ngrowing.onclick = function() {\n  this.style.fontSize = '36px';\n  this.style.color = 'red';\n};\n</script>\n```\n\nNow, let's cover animation properties one by one.\n\n## transition-property\n\nIn `transition-property`, we write a list of properties to animate, for instance: `left`, `margin-left`, `height`, `color`. Or we could write `all`, which means \"animate all properties\".\n\nDo note that, there are properties which can not be animated. However, [most of the generally used properties are animatable](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_animated_properties).\n\n## transition-duration\n\nIn `transition-duration` we can specify how long the animation should take. The time should be in [CSS time format](http://www.w3.org/TR/css3-values/#time): in seconds `s` or milliseconds `ms`.\n\n## transition-delay\n\nIn `transition-delay` we can specify the delay *before* the animation. For instance, if `transition-delay` is `1s` and `transition-duration` is `2s`, then the animation starts 1 second after the property change and the total duration will be 2 seconds.\n\nNegative values are also possible. Then the animation is shown immediately, but the starting point of the animation will be after given value (time). For example, if `transition-delay` is `-1s` and `transition-duration` is `2s`, then animation starts from the halfway point and total duration will be 1 second. \n\nHere the animation shifts numbers from `0` to `9` using CSS `translate` property:\n\n[codetabs src=\"digits\"]\n\nThe `transform` property is animated like this:\n\n```css\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n}\n```\n\nIn the example above JavaScript adds the class `.animate` to the element -- and the animation starts:\n\n```js\nstripe.classList.add('animate');\n```\n\nWe could also start it from somewhere in the middle of the transition, from an exact number, e.g. corresponding to the current second, using a negative `transition-delay`.\n\nHere if you click the digit -- it starts the animation from the current second:\n\n[codetabs src=\"digits-negative-delay\"]\n\nJavaScript does it with an extra line:\n\n```js\nstripe.onclick = function() {\n  let sec = new Date().getSeconds() % 10;\n*!*\n  // for instance, -3s here starts the animation from the 3rd second\n  stripe.style.transitionDelay = '-' + sec + 's';\n*/!*\n  stripe.classList.add('animate');\n};\n```\n\n## transition-timing-function\n\nThe timing function describes how the animation process is distributed along its timeline. Will it start slowly and then go fast, or vice versa.\n\nIt appears to be the most complicated property at first. But it becomes very simple if we devote a bit time to it.\n\nThat property accepts two kinds of values: a Bezier curve or steps. Let's start with the curve, as it's used more often.\n\n### Bezier curve\n\nThe timing function can be set as a [Bezier curve](/bezier-curve) with 4 control points that satisfy the conditions:\n\n1. First control point: `(0,0)`.\n2. Last control point: `(1,1)`.\n3. For intermediate points, the values of `x` must be in the interval `0..1`, `y` can be anything.\n\nThe syntax for a Bezier curve in CSS: `cubic-bezier(x2, y2, x3, y3)`. Here we need to specify only 2nd and 3rd control points, because the 1st one is fixed to `(0,0)` and the 4th one is `(1,1)`.\n\nThe timing function describes how fast the animation process goes.\n\n- The `x` axis is the time: `0` -- the start, `1` -- the end of `transition-duration`.\n- The `y` axis specifies the completion of the process: `0` -- the starting value of the property, `1` -- the final value.\n\nThe simplest variant is when the animation goes uniformly, with the same linear speed. That can be specified by the curve `cubic-bezier(0, 0, 1, 1)`.\n\nHere's how that curve looks:\n\n![](bezier-linear.svg)\n\n...As we can see, it's just a straight line. As the time (`x`) passes, the completion (`y`) of the animation steadily goes from `0` to `1`.\n\nThe train in the example below goes from left to right with the permanent speed (click it):\n\n[codetabs src=\"train-linear\"]\n\nThe CSS `transition` is based on that curve:\n\n```css\n.train {\n  left: 0;\n  transition: left 5s cubic-bezier(0, 0, 1, 1);\n  /* JavaScript sets left to 450px */\n}\n```\n\n...And how can we show a train slowing down?\n\nWe can use another Bezier curve: `cubic-bezier(0.0, 0.5, 0.5 ,1.0)`.\n\nThe graph:\n\n![](train-curve.svg)\n\nAs we can see, the process starts fast: the curve soars up high, and then slower and slower.\n\nHere's the timing function in action (click the train):\n\n[codetabs src=\"train\"]\n\nCSS:\n```css\n.train {\n  left: 0;\n  transition: left 5s cubic-bezier(0, .5, .5, 1);\n  /* JavaScript sets left to 450px */\n}\n```\n\nThere are several built-in curves: `linear`, `ease`, `ease-in`, `ease-out` and `ease-in-out`.\n\nThe `linear` is a shorthand for `cubic-bezier(0, 0, 1, 1)` -- a straight line, which we described above.\n\nOther names are shorthands for the following `cubic-bezier`:\n\n| <code>ease</code><sup>*</sup> | <code>ease-in</code> | <code>ease-out</code> | <code>ease-in-out</code> |\n|-------------------------------|----------------------|-----------------------|--------------------------|\n| <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> |\n| ![ease, figure](ease.svg) | ![ease-in, figure](ease-in.svg) | ![ease-out, figure](ease-out.svg) | ![ease-in-out, figure](ease-in-out.svg) |\n\n`*` -- by default, if there's no timing function, `ease` is used.\n\nSo we could use `ease-out` for our slowing down train:\n\n\n```css\n.train {\n  left: 0;\n  transition: left 5s ease-out;\n  /* transition: left 5s cubic-bezier(0, .5, .5, 1); */\n}\n```\n\nBut it looks a bit differently.\n\n**A Bezier curve can make the animation exceed its range.**\n\nThe control points on the curve can have any `y` coordinates: even negative or huge ones. Then the Bezier curve would also extend very low or high, making the animation go beyond its normal range.\n\nIn the example below the animation code is:\n```css\n.train {\n  left: 100px;\n  transition: left 5s cubic-bezier(.5, -1, .5, 2);\n  /* JavaScript sets left to 400px */\n}\n```\n\nThe property `left` should animate from `100px` to `400px`.\n\nBut if you click the train, you'll see that:\n\n- First, the train goes *back*: `left` becomes less than `100px`.\n- Then it goes forward, a little bit farther than `400px`.\n- And then back again -- to `400px`.\n\n[codetabs src=\"train-over\"]\n\nWhy it happens is pretty obvious if we look at the graph of the given Bezier curve:\n\n![](bezier-train-over.svg)\n\nWe moved the `y` coordinate of the 2nd point below zero, and for the 3rd point we made it over `1`, so the curve goes out of the \"regular\" quadrant. The `y` is out of the \"standard\" range `0..1`.\n\nAs we know, `y` measures \"the completion of the animation process\". The value `y = 0` corresponds to the starting property value and `y = 1` -- the ending value. So values `y<0` move the property beyond the starting `left` and `y>1` -- past the final `left`.\n\nThat's a \"soft\" variant for sure. If we put `y` values like `-99` and `99` then the train would jump out of the range much more.\n\nBut how do we make a Bezier curve for a specific task? There are many tools. For instance, we can do it on the site <http://cubic-bezier.com/>.\n\n### Steps\n\nThe timing function `steps(number of steps[, start/end])` allows splitting an animation into steps.\n\nLet's see that in an example with digits.\n\nHere's a list of digits, without any animations, just as a source:\n\n[codetabs src=\"step-list\"]\n\nWe'll make the digits appear in a discrete way by making the part of the list outside of the red \"window\" invisible and shifting the list to the left with each step.\n\nThere will be 9 steps, a step-move for each digit:\n\n```css\n#stripe.animate  {\n  transform: translate(-90%);\n  transition: transform 9s *!*steps(9, start)*/!*;\n}\n```\n\nIn action:\n\n[codetabs src=\"step\"]\n\nThe first argument of `steps(9, start)` is the number of steps. The transform will be split into 9 parts (10% each). The time interval is automatically divided into 9 parts as well, so `transition: 9s` gives us 9 seconds for the whole animation – 1 second per digit.\n\nThe second argument is one of two words: `start` or `end`.\n\nThe `start` means that in the beginning of animation we need to make the first step immediately.\n\nWe can observe that during the animation: when we click on the digit it changes to `1` (the first step) immediately, and then changes in the beginning of the next second.\n\nThe process is progressing like this:\n\n- `0s` -- `-10%` (first change in the beginning of the 1st second, immediately)\n- `1s` -- `-20%`\n- ...\n- `8s` -- `-80%`\n- (the last second shows the final value).\n\nThe alternative value `end` would mean that the change should be applied not in the beginning, but at the end of each second.\n\nSo the process would go like this:\n\n- `0s` -- `0`\n- `1s` -- `-10%` (first change at the end of the 1st second)\n- `2s` -- `-20%`\n- ...\n- `9s` -- `-90%`\n\nHere's `steps(9, end)` in action (note the pause between the first digit change):\n\n[codetabs src=\"step-end\"]\n\nThere are also shorthand values:\n\n- `step-start` -- is the same as `steps(1, start)`. That is, the animation starts immediately and takes 1 step. So it starts and finishes immediately, as if there were no animation.\n- `step-end` -- the same as `steps(1, end)`: make the animation in a single step at the end of `transition-duration`.\n\nThese values are rarely used, because that's not really animation, but rather a single-step change.\n\n## Event transitionend\n\nWhen the CSS animation finishes the `transitionend` event triggers.\n\nIt is widely used to do an action after the animation is done. Also we can join animations.\n\nFor instance, the ship in the example below starts to sail there and back when clicked, each time farther and farther to the right:\n\n[iframe src=\"boat\" height=300 edit link]\n\nThe animation is initiated by the function `go` that re-runs each time the transition finishes, and flips the direction:\n\n```js\nboat.onclick = function() {\n  //...\n  let times = 1;\n\n  function go() {\n    if (times % 2) {\n      // sail to the right\n      boat.classList.remove('back');\n      boat.style.marginLeft = 100 * times + 200 + 'px';\n    } else {\n      // sail to the left\n      boat.classList.add('back');\n      boat.style.marginLeft = 100 * times - 200 + 'px';\n    }\n\n  }\n\n  go();\n\n  boat.addEventListener('transitionend', function() {\n    times++;\n    go();\n  });\n};\n```\n\nThe event object for `transitionend` has a few specific properties:\n\n`event.propertyName`\n: The property that has finished animating. Can be good if we animate multiple properties simultaneously.\n\n`event.elapsedTime`\n: The time (in seconds) that the animation took, without `transition-delay`.\n\n## Keyframes\n\nWe can join multiple simple animations together using the `@keyframes` CSS rule.\n\nIt specifies the \"name\" of the animation and rules - what, when and where to animate. Then using the `animation` property, we can attach the animation to the element and specify additional parameters for it.\n\nHere's an example with explanations:\n\n```html run height=60 autorun=\"no-epub\" no-beautify\n<div class=\"progress\"></div>\n\n<style>\n*!*\n  @keyframes go-left-right {        /* give it a name: \"go-left-right\" */\n    from { left: 0px; }             /* animate from left: 0px */\n    to { left: calc(100% - 50px); } /* animate to left: 100%-50px */\n  }\n*/!*\n\n  .progress {\n*!*\n    animation: go-left-right 3s infinite alternate;\n    /* apply the animation \"go-left-right\" to the element\n       duration 3 seconds\n       number of times: infinite\n       alternate direction every time\n    */\n*/!*\n\n    position: relative;\n    border: 2px solid green;\n    width: 50px;\n    height: 20px;\n    background: lime;\n  }\n</style>\n```\n\nThere are many articles about `@keyframes` and a [detailed specification](https://drafts.csswg.org/css-animations/).\n\nYou probably won't need `@keyframes` often, unless everything is in constant motion on your sites.\n\n## Summary\n\nCSS animations allow smoothly (or not) animated changes of one or multiple CSS properties.\n\nThey are good for most animation tasks. We're also able to use JavaScript for animations, the next chapter is devoted to that.\n\nLimitations of CSS animations compared to JavaScript animations:\n\n```compare plus=\"CSS animations\" minus=\"JavaScript animations\"\n+ Simple things done simply.\n+ Fast and lightweight for CPU.\n- JavaScript animations are flexible. They can implement any animation logic, like an \"explosion\" of an element.\n- Not just property changes. We can create new elements in JavaScript as part of the animation.\n```\n\nThe majority of animations can be implemented using CSS as described in this chapter. And the `transitionend` event allows JavaScript to be run after the animation, so it integrates fine with the code.\n\nBut in the next chapter we'll do some JavaScript animations to cover more complex cases.\n"
  },
  {
    "path": "7-animation/2-css-animations/boat.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img src=\"https://js.cx/clipart/boat.png\" id=\"boat\">\n\n  <script>\n    boat.onclick = function() {\n\n      this.onclick = null; // only the first click should start the animation\n\n      let times = 1;\n\n      function go() {\n        if (times % 2) {\n          boat.classList.remove('back');\n          boat.style.marginLeft = 100 * times + 200 + 'px';\n        } else {\n          boat.classList.add('back');\n          boat.style.marginLeft = 100 * times - 200 + 'px';\n        }\n\n      }\n\n      go();\n\n      boat.addEventListener('transitionend', function() {\n        times++;\n        go();\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/boat.view/style.css",
    "content": "#boat {\n  margin-left: 0;\n  cursor: pointer;\n  transition: margin-left 3s ease-in-out;\n}\n\n/* flipping the picture with CSS */\n.back {\n  transform: scaleX(-1);\n  filter: fliph;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script src=\"script.js\"></script>\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/script.js",
    "content": "stripe.onclick = function() {\n  let sec = new Date().getSeconds() % 10;\n  stripe.style.transitionDelay = '-' + sec + 's';\n  stripe.classList.add('animate');\n};"
  },
  {
    "path": "7-animation/2-css-animations/digits-negative-delay.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: linear;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script src=\"script.js\"></script>\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/script.js",
    "content": "stripe.onclick = function() {\n  stripe.classList.add('animate');\n};"
  },
  {
    "path": "7-animation/2-css-animations/digits.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: linear;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step-end.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script>\n    digit.onclick = function() {\n      stripe.classList.add('animate');\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step-end.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: steps(9, end);\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step-list.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n</body>\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step-list.view/style.css",
    "content": "#digit {\n  border: 1px solid red;\n  width: 1.2em;\n}\n\n#stripe {\n  display: inline-block;\n  font: 32px monospace;\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/step.view/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  Click below to animate:\n\n  <div id=\"digit\"><div id=\"stripe\">0123456789</div></div>\n\n  <script>\n    digit.onclick = function() {\n      stripe.classList.add('animate');\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/2-css-animations/step.view/style.css",
    "content": "#digit {\n  width: .5em;\n  overflow: hidden;\n  font: 32px monospace;\n  cursor: pointer;\n}\n\n#stripe {\n  display: inline-block\n}\n\n#stripe.animate {\n  transform: translate(-90%);\n  transition-property: transform;\n  transition-duration: 9s;\n  transition-timing-function: steps(9, start);\n}\n"
  },
  {
    "path": "7-animation/2-css-animations/train-linear.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='450px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train-linear.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 0;\n  transition: left 5s cubic-bezier(0, 0, 1, 1);\n}"
  },
  {
    "path": "7-animation/2-css-animations/train-over.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='400px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train-over.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 100px;\n  transition: left 5s cubic-bezier(.5, -1, .5, 2);\n}"
  },
  {
    "path": "7-animation/2-css-animations/train.view/index.html",
    "content": "<!doctype html>\n<html>\n\n<head>\n  <meta charset=\"UTF-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n  <img class=\"train\" src=\"https://js.cx/clipart/train.gif\" onclick=\"this.style.left='450px'\">\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/2-css-animations/train.view/style.css",
    "content": ".train {\n  position: relative;\n  cursor: pointer;\n  width: 177px;\n  height: 160px;\n  left: 0px;\n  transition: left 5s cubic-bezier(0.0, 0.5, 0.5, 1.0);\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.md",
    "content": "Untuk membuatnya memantul kita bisa menggunakan properti CSS `top` dan `position:absolute` untuk bola yang berada didalam bidang dengan `position:relative`.\n\nKoordinat bawah dari bidangnya adalah `field.clientHeight`. Properti CSS `top` mengacu pada bagian atas dari bolanya. Jadi itu haruslah berasal dari `0` sampai `field.clientHeight - ball. clientHeight`, itulah yang menjadi posisi terbawah dari bagian atas bolanya.\n\nUntuk mendapatkan efek \"memantul\" kita bisa menggunakan fungsi waktu `bounce` di mode `easeOut`.\n\nIni adalah kode akhir dari animasinya:\n\n```js\nlet to = field.clientHeight - ball.clientHeight;\n\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(bounce),\n  draw(progress) {\n    ball.style.top = to * progress + 'px'\n  }\n});\n```\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    ball.onclick = function() {\n\n      let to = field.clientHeight - ball.clientHeight;\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(bounce),\n        draw(progress) {\n          ball.style.top = to * progress + 'px'\n        }\n      });\n\n\n    };\n  </script>\n\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/solution.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/source.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://en.js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://en.js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/source.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/1-animate-ball/task.md",
    "content": "Nilai: 5\n\n---\n\n# Animasikan bola yang memantul\n\nBuatlah bola yang memantul. Klik untuk melihat contohnya:\n\n[iframe height=250 src=\"solution\"]\n"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.md",
    "content": "In the task <info:task/animate-ball> we had only one property to animate. Now we need one more: `elem.style.left`.\n\nThe horizontal coordinate changes by another law: it does not \"bounce\", but gradually increases shifting the ball to the right.\n\nWe can write one more `animate` for it.\n\nAs the time function we could use `linear`, but something like `makeEaseOut(quad)` looks much better.\n\nThe code:\n\n```js\nlet height = field.clientHeight - ball.clientHeight;\nlet width = 100;\n\n// animate top (bouncing)\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(bounce),\n  draw: function(progress) {\n    ball.style.top = height * progress + 'px'\n  }\n});\n\n// animate left (moving to the right)\nanimate({\n  duration: 2000,\n  timing: makeEaseOut(quad),\n  draw: function(progress) {\n    ball.style.left = width * progress + \"px\"\n  }\n});\n```\n"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n  <link rel=\"stylesheet\" href=\"style.css\">\n</head>\n\n<body>\n\n\n  <div id=\"field\">\n    <img src=\"https://js.cx/clipart/ball.svg\" width=\"40\" height=\"40\" id=\"ball\">\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    function quad(timeFraction) {\n      return Math.pow(timeFraction, 2);\n    }\n\n    ball.onclick = function() {\n\n      let height = field.clientHeight - ball.clientHeight;\n      let width = 100;\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(bounce),\n        draw: function(progress) {\n          ball.style.top = height * progress + 'px'\n        }\n      });\n\n      animate({\n        duration: 2000,\n        timing: makeEaseOut(quad),\n        draw: function(progress) {\n          ball.style.left = width * progress + \"px\"\n        }\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/solution.view/style.css",
    "content": "#field {\n  height: 200px;\n  border-bottom: 3px black groove;\n  position: relative;\n}\n\n#ball {\n  position: absolute;\n  cursor: pointer;\n}"
  },
  {
    "path": "7-animation/3-js-animation/2-animate-ball-hops/task.md",
    "content": "importance: 5\n\n---\n\n# Animate the ball bouncing to the right\n\nMake the ball bounce to the right. Like this:\n\n[iframe height=250 src=\"solution\"]\n\nWrite the animation code. The distance to the left is `100px`.\n\nTake the solution of the previous task <info:task/animate-ball> as the source.\n"
  },
  {
    "path": "7-animation/3-js-animation/article.md",
    "content": "# JavaScript animations\n\nJavaScript animations can handle things that CSS can't.\n\nFor instance, moving along a complex path, with a timing function different from Bezier curves, or an animation on a canvas.\n\n## Using setInterval\n\nAn animation can be implemented as a sequence of frames -- usually small changes to HTML/CSS properties.\n\nFor instance, changing `style.left` from `0px` to `100px` moves the element. And if we increase it in `setInterval`, changing by `2px` with a tiny delay, like 50 times per second, then it looks smooth. That's the same principle as in the cinema: 24 frames per second is enough to make it look smooth.\n\nThe pseudo-code can look like this:\n\n```js\nlet timer = setInterval(function() {\n  if (animation complete) clearInterval(timer);\n  else increase style.left by 2px\n}, 20); // change by 2px every 20ms, about 50 frames per second\n```\n\nMore complete example of the animation:\n\n```js\nlet start = Date.now(); // remember start time\n\nlet timer = setInterval(function() {\n  // how much time passed from the start?\n  let timePassed = Date.now() - start;\n\n  if (timePassed >= 2000) {\n    clearInterval(timer); // finish the animation after 2 seconds\n    return;\n  }\n\n  // draw the animation at the moment timePassed\n  draw(timePassed);\n\n}, 20);\n\n// as timePassed goes from 0 to 2000\n// left gets values from 0px to 400px\nfunction draw(timePassed) {\n  train.style.left = timePassed / 5 + 'px';\n}\n```\n\nClick for the demo:\n\n[codetabs height=200 src=\"move\"]\n\n## Using requestAnimationFrame\n\nLet's imagine we have several animations running simultaneously.\n\nIf we run them separately, then even though each one has `setInterval(..., 20)`, then the browser would have to repaint much more often than every `20ms`.\n\nThat's because they have different starting time, so \"every 20ms\" differs between different animations. The intervals are not aligned. So we'll have several independent runs within `20ms`.\n\nIn other words, this:\n\n```js\nsetInterval(function() {\n  animate1();\n  animate2();\n  animate3();\n}, 20)\n```\n\n...Is lighter than three independent calls:\n\n```js\nsetInterval(animate1, 20); // independent animations\nsetInterval(animate2, 20); // in different places of the script\nsetInterval(animate3, 20);\n```\n\nThese several independent redraws should be grouped together, to make the redraw easier for the browser and hence load less CPU load and look smoother.\n\nThere's one more thing to keep in mind. Sometimes CPU is overloaded, or there are other reasons to redraw less often (like when the browser tab is hidden), so we really shouldn't run it every `20ms`.\n\nBut how do we know about that in JavaScript? There's a specification [Animation timing](http://www.w3.org/TR/animation-timing/) that provides the function `requestAnimationFrame`. It addresses all these issues and even more.\n\nThe syntax:\n```js\nlet requestId = requestAnimationFrame(callback)\n```\n\nThat schedules the `callback` function to run in the closest time when the browser wants to do animation.\n\nIf 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.\n\nThe returned value `requestId` can be used to cancel the call:\n```js\n// cancel the scheduled execution of callback\ncancelAnimationFrame(requestId);\n```\n\nThe `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).\n\nUsually `callback` runs very soon, unless the CPU is overloaded or the laptop battery is almost discharged, or there's another reason.\n\nThe code below shows the time between first 10 runs for `requestAnimationFrame`. Usually it's 10-20ms:\n\n```html run height=40 refresh\n<script>\n  let prev = performance.now();\n  let times = 0;\n\n  requestAnimationFrame(function measure(time) {\n    document.body.insertAdjacentHTML(\"beforeEnd\", Math.floor(time - prev) + \" \");\n    prev = time;\n\n    if (times++ < 10) requestAnimationFrame(measure);\n  })\n</script>\n```\n\n## Structured animation\n\nNow we can make a more universal animation function based on `requestAnimationFrame`:\n\n```js\nfunction animate({timing, draw, duration}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    // timeFraction goes from 0 to 1\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    // calculate the current animation state\n    let progress = timing(timeFraction)\n\n    draw(progress); // draw it\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n```\n\nFunction `animate` accepts 3 parameters that essentially describes the animation:\n\n`duration`\n: Total time of animation. Like, `1000`.\n\n`timing(timeFraction)`\n: 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).\n\n    For instance, a linear function means that the animation goes on uniformly with the same speed:\n\n    ```js\n    function linear(timeFraction) {\n      return timeFraction;\n    }\n    ```\n\n    It's graph:\n    ![](linear.svg)\n\n    That's just like `transition-timing-function: linear`. There are more interesting variants shown below.\n\n`draw(progress)`\n: 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.\n\n    This is that function that actually draws out the animation.\n\n    It can move the element:\n    ```js\n    function draw(progress) {\n      train.style.left = progress + 'px';\n    }\n    ```\n\n    ...Or do anything else, we can animate anything, in any way.\n\n\nLet's animate the element `width` from `0` to `100%` using our function.\n\nClick on the element for the demo:\n\n[codetabs height=60 src=\"width\"]\n\nThe code for it:\n\n```js\nanimate({\n  duration: 1000,\n  timing(timeFraction) {\n    return timeFraction;\n  },\n  draw(progress) {\n    elem.style.width = progress * 100 + '%';\n  }\n});\n```\n\nUnlike 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.\n\n## Timing functions\n\nWe saw the simplest, linear timing function above.\n\nLet's see more of them. We'll try movement animations with different timing functions to see how they work.\n\n### Power of n\n\nIf we want to speed up the animation, we can use `progress` in the power `n`.\n\nFor instance, a parabolic curve:\n\n```js\nfunction quad(timeFraction) {\n  return Math.pow(timeFraction, 2)\n}\n```\n\nThe graph:\n\n![](quad.svg)\n\nSee in action (click to activate):\n\n[iframe height=40 src=\"quad\" link]\n\n...Or the cubic curve or even greater `n`. Increasing the power makes it speed up faster.\n\nHere's the graph for `progress` in the power `5`:\n\n![](quint.svg)\n\nIn action:\n\n[iframe height=40 src=\"quint\" link]\n\n### The arc\n\nFunction:\n\n```js\nfunction circ(timeFraction) {\n  return 1 - Math.sin(Math.acos(timeFraction));\n}\n```\n\nThe graph:\n\n![](circ.svg)\n\n[iframe height=40 src=\"circ\" link]\n\n### Back: bow shooting\n\nThis function does the \"bow shooting\". First we \"pull the bowstring\", and then \"shoot\".\n\nUnlike previous functions, it depends on an additional parameter `x`, the \"elasticity coefficient\". The distance of \"bowstring pulling\" is defined by it.\n\nThe code:\n\n```js\nfunction back(x, timeFraction) {\n  return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)\n}\n```\n\n**The graph for `x = 1.5`:**\n\n![](back.svg)\n\nFor animation we use it with a specific value of `x`. Example for `x = 1.5`:\n\n[iframe height=40 src=\"back\" link]\n\n### Bounce\n\nImagine we are dropping a ball. It falls down, then bounces back a few times and stops.\n\nThe `bounce` function does the same, but in the reverse order: \"bouncing\" starts immediately. It uses few special coefficients for that:\n\n```js\nfunction bounce(timeFraction) {\n  for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n    if (timeFraction >= (7 - 4 * a) / 11) {\n      return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n    }\n  }\n}\n```\n\nIn action:\n\n[iframe height=40 src=\"bounce\" link]\n\n### Elastic animation\n\nOne more \"elastic\" function that accepts an additional parameter `x` for the \"initial range\".\n\n```js\nfunction elastic(x, timeFraction) {\n  return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction)\n}\n```\n\n**The graph for `x=1.5`:**\n![](elastic.svg)\n\nIn action for `x=1.5`:\n\n[iframe height=40 src=\"elastic\" link]\n\n## Reversal: ease*\n\nSo we have a collection of timing functions. Their direct application is called \"easeIn\".\n\nSometimes we need to show the animation in the reverse order. That's done with the \"easeOut\" transform.\n\n### easeOut\n\nIn the \"easeOut\" mode the `timing` function is put into a wrapper `timingEaseOut`:\n\n```js\ntimingEaseOut(timeFraction) = 1 - timing(1 - timeFraction)\n```\n\nIn other words, we have a \"transform\" function `makeEaseOut` that takes a \"regular\" timing function and returns the wrapper around it:\n\n```js\n// accepts a timing function, returns the transformed variant\nfunction makeEaseOut(timing) {\n  return function(timeFraction) {\n    return 1 - timing(1 - timeFraction);\n  }\n}\n```\n\nFor instance, we can take the `bounce` function described above and apply it:\n\n```js\nlet bounceEaseOut = makeEaseOut(bounce);\n```\n\nThen the bounce will be not in the beginning, but at the end of the animation. Looks even better:\n\n[codetabs src=\"bounce-easeout\"]\n\nHere we can see how the transform changes the behavior of the function:\n\n![](bounce-inout.svg)\n\nIf there's an animation effect in the beginning, like bouncing -- it will be shown at the end.\n\nIn 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.\n\n- Regular bounce -- the object bounces at the bottom, then at the end sharply jumps to the top.\n- After `easeOut` -- it first jumps to the top, then bounces there.\n\n### easeInOut\n\nWe also can show the effect both in the beginning and the end of the animation. The transform is called \"easeInOut\".\n\nGiven the timing function, we calculate the animation state like this:\n\n```js\nif (timeFraction <= 0.5) { // first half of the animation\n  return timing(2 * timeFraction) / 2;\n} else { // second half of the animation\n  return (2 - timing(2 * (1 - timeFraction))) / 2;\n}\n```\n\nThe wrapper code:\n\n```js\nfunction makeEaseInOut(timing) {\n  return function(timeFraction) {\n    if (timeFraction < .5)\n      return timing(2 * timeFraction) / 2;\n    else\n      return (2 - timing(2 * (1 - timeFraction))) / 2;\n  }\n}\n\nbounceEaseInOut = makeEaseInOut(bounce);\n```\n\nIn action, `bounceEaseInOut`:\n\n[codetabs src=\"bounce-easeinout\"]\n\nThe \"easeInOut\" transform joins two graphs into one: `easeIn` (regular) for the first half of the animation and `easeOut` (reversed) -- for the second part.\n\nThe effect is clearly seen if we compare the graphs of `easeIn`, `easeOut` and `easeInOut` of the `circ` timing function:\n\n![](circ-ease.svg)\n\n- <span style=\"color:#EE6B47\">Red</span> is the regular variant of `circ` (`easeIn`).\n- <span style=\"color:#8DB173\">Green</span> -- `easeOut`.\n- <span style=\"color:#62C0DC\">Blue</span> -- `easeInOut`.\n\nAs 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.\n\n## More interesting \"draw\"\n\nInstead of moving the element we can do something else. All we need is to write the proper `draw`.\n\nHere's the animated \"bouncing\" text typing:\n\n[codetabs src=\"text\"]\n\n## Summary\n\nFor animations that CSS can't handle well, or those that need tight control, JavaScript can help. JavaScript animations 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.\n\nWhen 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.\n\nHere's the helper `animate` function to setup most animations:\n\n```js\nfunction animate({timing, draw, duration}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    // timeFraction goes from 0 to 1\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    // calculate the current animation state\n    let progress = timing(timeFraction);\n\n    draw(progress); // draw it\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n```\n\nOptions:\n\n- `duration` -- the total animation time in ms.\n- `timing` -- the function to calculate animation progress. Gets a time fraction from 0 to 1, returns the animation progress, usually from 0 to 1.\n- `draw` -- the function to draw the animation.\n\nSurely 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.\n\nJavaScript 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.\n\nThe same is about `draw`: we can animate anything, not just CSS properties.\n"
  },
  {
    "path": "7-animation/3-js-animation/back.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function back(x, timeFraction) {\n          return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)\n        }.bind(null, 1.5),\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/back.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeinout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    function makeEaseInOut(timing) {\n      return function(timeFraction) {\n        if (timeFraction < .5)\n          return timing(2 * timeFraction) / 2;\n        else\n          return (2 - timing(2 * (1 - timeFraction))) / 2;\n      }\n    }\n\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    let bounceEaseInOut = makeEaseInOut(bounce);\n\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: bounceEaseInOut,\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeinout.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeout.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    function makeEaseOut(timing) {\n      return function(timeFraction) {\n        return 1 - timing(1 - timeFraction);\n      }\n    }\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n\n    let bounceEaseOut = makeEaseOut(bounce);\n\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: bounceEaseOut,\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce-easeout.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/bounce.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: function bounce(timeFraction) {\n          for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n            if (timeFraction >= (7 - 4 * a) / 11) {\n              return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n            }\n          }\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/bounce.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/circ.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function circ(timeFraction) {\n          return 1 - Math.sin(Math.acos(timeFraction))\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/circ.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/elastic.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 3000,\n        timing: function elastic(x, timeFraction) {\n          return Math.pow(2, 10 * (timeFraction - 1)) * Math.cos(20 * Math.PI * x / 3 * timeFraction)\n        }.bind(null, 1.5),\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/elastic.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/move-raf.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #train {\n      position: relative;\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"train\" src=\"https://en.js.cx/clipart/train.gif\">\n\n  <script>\n    train.onclick = function() {\n      animate(function(progress) {\n        train.style.left = progress * 400 + 'px';\n      }, 2000);\n    };\n\n\n    function animate(draw, duration) {\n      let start = performance.now();\n\n      requestAnimationFrame(function animate(time) {\n        // how much time passed from the start?\n        let timePassed = time - start;\n\n        if (timePassed > duration) timePassed = duration;\n\n        // progress is from 0 to 1, the fraction of time that passed\n        let progress = duration / timePassed;\n\n        // draw the animation progress\n        draw(progress);\n\n        // if time is not up - schedule one more run\n        if (timePassed < duration) {\n          requestAnimationFrame(animate);\n        }\n\n      });\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/move.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <style>\n    #train {\n      position: relative;\n      cursor: pointer;\n    }\n  </style>\n</head>\n\n<body>\n\n  <img id=\"train\" src=\"https://js.cx/clipart/train.gif\">\n\n\n  <script>\n    train.onclick = function() {\n      let start = Date.now();\n\n      let timer = setInterval(function() {\n        let timePassed = Date.now() - start;\n\n        train.style.left = timePassed / 5 + 'px';\n\n        if (timePassed > 2000) clearInterval(timer);\n\n      }, 20);\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/quad.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return Math.pow(timeFraction, 2);\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/quad.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/quint.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <div id=\"path\">\n    <div id=\"brick\"></div>\n  </div>\n\n  <script>\n    brick.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return Math.pow(timeFraction, 5);\n        },\n        draw: function(progress) {\n          brick.style.left = progress * 500 + 'px';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>"
  },
  {
    "path": "7-animation/3-js-animation/quint.view/style.css",
    "content": "#brick {\n  width: 40px;\n  height: 20px;\n  background: #EE6B47;\n  position: relative;\n  cursor: pointer;\n}\n\n#path {\n  outline: 1px solid #E8C48E;\n  width: 540px;\n  height: 20px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/text.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <link rel=\"stylesheet\" href=\"style.css\">\n  <script src=\"https://js.cx/libs/animate.js\"></script>\n</head>\n\n<body>\n\n\n  <textarea id=\"textExample\" rows=\"5\" cols=\"60\">He took his vorpal sword in hand:\nLong time the manxome foe he sought—\nSo rested he by the Tumtum tree,\nAnd stood awhile in thought.\n  </textarea>\n\n  <button onclick=\"animateText(textExample)\">Run the animated typing!</button>\n\n  <script>\n    function animateText(textArea) {\n      let text = textArea.value;\n      let to = text.length,\n        from = 0;\n\n      animate({\n        duration: 5000,\n        timing: bounce,\n        draw: function(progress) {\n          let result = (to - from) * progress + from;\n          textArea.value = text.substr(0, Math.ceil(result))\n        }\n      });\n    }\n\n\n    function bounce(timeFraction) {\n      for (let a = 0, b = 1, result; 1; a += b, b /= 2) {\n        if (timeFraction >= (7 - 4 * a) / 11) {\n          return -Math.pow((11 - 6 * a - 11 * timeFraction) / 4, 2) + Math.pow(b, 2)\n        }\n      }\n    }\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/3-js-animation/text.view/style.css",
    "content": "textarea {\n  display: block;\n  border: 1px solid #BBB;\n  color: #444;\n  font-size: 110%;\n}\n\nbutton {\n  margin-top: 10px;\n}"
  },
  {
    "path": "7-animation/3-js-animation/width.view/animate.js",
    "content": "function animate({duration, draw, timing}) {\n\n  let start = performance.now();\n\n  requestAnimationFrame(function animate(time) {\n    let timeFraction = (time - start) / duration;\n    if (timeFraction > 1) timeFraction = 1;\n\n    let progress = timing(timeFraction)\n\n    draw(progress);\n\n    if (timeFraction < 1) {\n      requestAnimationFrame(animate);\n    }\n\n  });\n}\n"
  },
  {
    "path": "7-animation/3-js-animation/width.view/index.html",
    "content": "<!DOCTYPE HTML>\n<html>\n\n<head>\n  <meta charset=\"utf-8\">\n  <style>\n    progress {\n      width: 5%;\n    }\n  </style>\n  <script src=\"animate.js\"></script>\n</head>\n\n<body>\n\n\n  <progress id=\"elem\"></progress>\n\n  <script>\n    elem.onclick = function() {\n      animate({\n        duration: 1000,\n        timing: function(timeFraction) {\n          return timeFraction;\n        },\n        draw: function(progress) {\n          elem.style.width = progress * 100 + '%';\n        }\n      });\n    };\n  </script>\n\n\n</body>\n\n</html>\n"
  },
  {
    "path": "7-animation/index.md",
    "content": "# Animation\n\nCSS and JavaScript animations.\n"
  },
  {
    "path": "8-web-components/1-webcomponents-intro/article.md",
    "content": "# Dari Ketinggian Orbital\n\nBagian ini menjelaskan seperangkat standar modern untuk \"komponen web\".\n\nSampai sekarang, standar ini sedang dalam pengembangan. Beberapa fitur didukung dengan baik dan terintegrasi\nke dalam standar HTML/DOM modern, sementara yang lainnya masih dalam tahap konsep. Anda dapat mencoba contohnya \ndi peramban apa pun, Google Chrome mungkin yang paling muktahir dengan fitur-fitur ini. Coba tebak, itu karena\nrekan Google berada di balik banyaknya spesifikasi terkait.\n\n## Apa yang sama di antaranya...\n\nIde keseluruhan komponen bukanlah hal baru. Ini digunakan dalam banyak kerangka kerja dan di tempat lain.\n\nSebelum kita beralih ke detail implementasi, lihatlah pencapaian besar umat manusia ini:\n\n![](satellite.jpg)\n\nItu adalah Stasiun Luar Angkasa Internasional (ISS).\n\nDan ini adalah bagaimana di dalamnya dibuat (kira-kira):\n\n![](satellite-expanded.jpg)\n\nStasiun Luar Angkasa Internasional:\n- Terdiri dari banyak komponen.\n- Setiap komponen, pada bagiannya, memiliki banyak detail kecil di dalamnya.\n- Komponennya sangat kompleks, jauh lebih rumit dibanding kebanyakan situs web.\n- Komponennya dikembangkan secara internasional, oleh tim dari berbagai negara, yang berbicara dalam bahasa yang berbeda.\n\n...Dan benda ini terbang, membuat manusia tetap hidup di luar angkasa!\n\nBagaimana perangkat rumit seperti itu dibuat?\n\nPrinsip mana yang bisa kita pinjam agar membuat pengembangan kita pada tingkat yang sama handal dan terukur? Atau, setidaknya, mendekati.\n\n## Arsitektur Komponen\n\nAturan terkenal untuk mengembangkan perangkat lunak yang kompleks adalah: jangan membuat perangkat lunak yang kompleks.\n\nJika sesuatu menjadi rumit -- bagi menjadi beberapa bagian yang lebih sederhana dan hubungkan dengan cara yang paling jelas.\n\n**Seorang arsitek yang baik adalah orang yang dapat membuat yang kompleks menjadi lebih sederhana.**\n\nKita dapat membagi antarmuka pengguna menjadi komponen visual: masing-masing komponen  memiliki tempat tersendiri di laman, bisa \"melakukan\" tugas yang telah dijelaskan dengan baik, dan terpisah dari yang lain.\n\nMari kita lihat situs web, misalnya Twitter.\n\nSecara alami terbagi menjadi beberapa komponen:\n\n![](web-components-twitter.svg)\n\n1. Navigasi atas\n2. Info pengguna.\n3. Saran untuk diikuti.\n4. Form pengiriman.\n5. (dan juga 6, 7) -- pesan-pesan.\n\nKomponen mungkin mempunyai subkomponen, misalnya pesan mungkin merupakan bagian dari komponen \"daftar pesan\" yang lebih tinggi. Gambar pengguna yang dapat diklik itu sendiri mungkin sebuah komponen, dan seterusnya.\n\nBagaimana kita menentukan, apa itu komponen? Itu berasal dari intuisi, pengalaman, dan akal sehat. Biasanya komponen itu adalah entitas visual yang terpisah yang bisa kita deskripsikan dalam hal apa yang dilakukannya dan bagaimana komponen ini berinteraksi dengan laman. Dalam kasus di atas, laman memiliki blok-blok, masing-masing memainkan perannya sendiri, itu logis untuk membuat komponen ini.\n\nSebuah komponen memiliki:\n- kelas JavaScript-nya sendiri.\n- Struktur DOM, dikelola hanya oleh kelasnya, kode di luar tidak bisa mengaksesnya (prinsip \"enkapsulasi\").\n- CSS styles, diterapkan pada komponen.\n- API: events, kelas methods dll, untuk berinteraksi dengan komponen lain.\n\nSekali lagi, keseluruhan \"komponen\" bukanlah sesuatu yang istimewa.\n\nAda banyak *framework* dan metodologi pengembangan untuk membangunnya, masing-masing dengan keunikannya sendiri. Biasanya, kelas dan konvensi CSS khusus digunakan untuk memberikan \"component feel\" -- CSS scoping dan enkapsulasi DOM.\n\n\"Komponen web\" menyediakan kemampuan browser bawaan untuk itu, jadi kita tidak perlu untuk menirunya lagi.\n\n- [Custom elements](https://html.spec.whatwg.org/multipage/custom-elements.html#custom-elements) -- untuk menetapkan elemen HTML kustom.\n- [Shadow DOM](https://dom.spec.whatwg.org/#shadow-trees) -- untuk membuat DOM internal untuk komponen, tersembunyi dari yang lain.\n- [CSS Scoping](https://drafts.csswg.org/css-scoping/) -- untuk mendeklarasikan *styles* yang hanya berlaku di dalam Shadow DOM komponen.\n- [Event retargeting](https://dom.spec.whatwg.org/#retarget) dan hal-hal kecil lainnnya untuk membuat komponen kustom lebih sesuai dengan pengembangan.\n\nDi bab berikutnya, kita akan membahas lebih detail tentang \"Custom Elements\" -- fitur komponen web yang mendasar dan didukung dengan baik, dengan sendirinya.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.md",
    "content": "\nTolong dicatat:\n1. Kita menghapus timer `setInterval` ketika elemen dihapus dari dokumen. Ini penting, jika tidak, timer terus berdetak bahkan jika tidak diperlukan lagi. Dan browser tidak dapat menghapus memori dari elemen ini dan yang direferensikan olehnya.\n2. Kita dapat mengakses tanggal sekarang sebagai properti `elem.date`. Semua *methods* dan properti kelas secara alami adalah *methods* dan properti elemen.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/index.html",
    "content": "<!doctype html>\n<script src=\"time-formatted.js\"></script>\n<script src=\"live-timer.js\"></script>\n\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/live-timer.js",
    "content": "class LiveTimer extends HTMLElement {\n\n  render() {\n    this.innerHTML = `\n    <time-formatted hour=\"numeric\" minute=\"numeric\" second=\"numeric\">\n    </time-formatted>\n    `;\n\n    this.timerElem = this.firstElementChild;\n  }\n\n  connectedCallback() { // (2)\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n    this.timer = setInterval(() => this.update(), 1000);\n  }\n\n  update() {\n    this.date = new Date();\n    this.timerElem.setAttribute('datetime', this.date);\n    this.dispatchEvent(new CustomEvent('tick', { detail: this.date }));\n  }\n\n  disconnectedCallback() {\n    clearInterval(this.timer); // important to let the element be garbage-collected\n  }\n\n}\n\ncustomElements.define(\"live-timer\", LiveTimer);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/solution.view/time-formatted.js",
    "content": "class TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() {\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() {\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { \n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/index.html",
    "content": "<!doctype html>\n<!-- don't modify this -->\n<script src=\"time-formatted.js\"></script>\n\n<!-- your code here: -->\n<script src=\"live-timer.js\"></script>\n\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/live-timer.js",
    "content": "class LiveTimer extends HTMLElement {\n\n  /* your code here */\n\n}\n\ncustomElements.define(\"live-timer\", LiveTimer);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/source.view/time-formatted.js",
    "content": "class TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() {\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() {\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { \n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n"
  },
  {
    "path": "8-web-components/2-custom-elements/1-live-timer/task.md",
    "content": "\n# Elemen live timer\n\nKita sudah memiliki elemen `<time-formatted>` untuk menunjukkan waktu yang diformat dengan baik.\n\nBuat elemen `<live-timer>` untuk menunjukkan waktu saat ini:\n1. `<live-timer>` harus menggunakan `<time-formatted>` secara internal, bukan menduplikasi fungsinya.\n2. Berdetak (update) setiap detik.\n3. Untuk setiap detik, sebuah *events* kustom bernama `tick` harus dibuat, dengan tanggal saat ini di `event.detail` (lihat bab <info: dispatch-events>).\n\nPenggunaan:\n\n```html\n<live-timer id=\"elem\"></live-timer>\n\n<script>\n  elem.addEventListener('tick', event => console.log(event.detail));\n</script>\n```\n\nDemo:\n\n[iframe src=\"solution\" height=40]\n"
  },
  {
    "path": "8-web-components/2-custom-elements/article.md",
    "content": "\n# Elemen kustom\n\nKita dapat membuat elemen HTML kustom, yang dideskripsikan dengan sebuah kelas, dengan *method* dan *properties*-nya sendiri, *events*, dan sebagainya.\n\nSetelah elemen kustom didefiniskan, kita dapat menggunakannya seperti elemen HTML bawaan.\n\nItu bagus, karena kamus HTML itu kaya, tetapi tidak terbatas. Tidak ada `<easy-tabs>`, `<sliding-carousel>`, `<beautiful-upload>` ... Coba pikirkan tag lain yang mungkin kita perlukan.\n\nKita bisa mendefinisikan elemen HTML kustom dengan sebuah kelas khusus dan kemudian menggunakannya seolah-olah sudah menjadi bagian dari HTML.\n\nAda dua jenis custom elements:\n\n1. **Autonomous custom elements** -- elemen yang \"semuanya baru\", *extending* kelas `HTMLElement` abstrak.\n2. **Customized built-in elements** -- *extending* elemen bawaan, seperti tombol yang disesuaikan, berdasarkan `HTMLButtonElement` dll.\n\nPertama kita akan membahas *Autonomus elements* dan kemudian beralih ke *Customized built-in elements*. \n\nUntuk membuat sebuah elemen kustom, kita perlu memberi tahu browser beberapa detail mengenai: cara menampilkannya, apa yang harus dilakukan saat elemen ditambahkan atau dihapus ke halaman, dll.\n\nItu dilakukan dengan membuat kelas dengan *method* khusus. Ini mudah, karena hanya ada beberapa *method*, dan semuanya opsional.\n\nBerikut gambaran dengan *method* lengkapnya:\n\n```js\nclass MyElement extends HTMLElement {\n  constructor() {\n    super();\n    // elemen dibuat\n  }\n\n  connectedCallback() {\n    // browser memanggil method ini ketika elemen ditambahkan ke dokumen\n    // (can be called many times if an element is repeatedly added/removed)\n  }\n\n  disconnectedCallback() {\n    // browser calls this method when the element is removed from the document\n    // (dapat dipanggil berkali-kali jika sebuah elemen ditambahkan / dihilangkan berulang kali)\n  }\n\n  static get observedAttributes() {\n    return [/* senarai nama atribut untuk memantau perubahan */];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) {\n    // dipanggil ketika salah satu atribut yang tercantum di atas diubah\n  }\n\n  adoptedCallback() {\n    // dipanggil saat elemen dipindahkan ke dokumen baru\n    // (terjadi di document.adoptNode, sangat jarang digunakan)\n  }\n\n  // mungkin ada elemen method dan properti lainnya\n}\n```\n\nSetelah itu, kita perlu mendaftarkan elemen tersebut:\n\n```js\n// memberi tahu browser bahwa <my-element> digunakan oleh kelas baru kita\ncustomElements.define(\"my-element\", MyElement);\n```\n\nSekarang untuk setiap elemen HTML dengan tag `<my-element>`, sebuah *instance* dari `MyElement` dibuat, dan *method* yang disebutkan di atas dipanggil. Kita juga bisa menggunakan `document.createElement ('my-element')` di JavaScript.\n\n```smart header=\"Custom element name must contain a hyphen `-`\"\nNama elemen kustom harus memiliki tanda hubung `-`, misalnya `my-element` dan `super-button` adalah nama yang valid, tapi `myelement` tidak.\n\nItu untuk memastikan bahwa tidak ada konflik nama antara elemen HTML bawaan dan kustom. \n```\n\n## Contoh: \"time-formatted\"\n\nMisalnya, sudah ada elemen `<time>` di HTML, untuk tanggal/waktu. Tetapi itu tidak bisa melakukan pemformatan apa pun dengan sendirinya.\n\nMari buat elemen `<time-formatted>` yang menampilkan waktu dalam format yang baik dan bahasa yang baik:\n\n```html run height=50 autorun=\"no-epub\"\n<script>\n*!*\nclass TimeFormatted extends HTMLElement { // (1)\n*/!*\n\n  connectedCallback() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n}\n\n*!*\ncustomElements.define(\"time-formatted\", TimeFormatted); // (2)\n*/!*\n</script>\n\n<!-- (3) -->\n*!*\n<time-formatted datetime=\"2019-12-01\"\n*/!*\n  year=\"numeric\" month=\"long\" day=\"numeric\"\n  hour=\"numeric\" minute=\"numeric\" second=\"numeric\"\n  time-zone-name=\"short\"\n></time-formatted>\n```\n\n1. Kelas diatas hanya memiliki satu *method* `connectedCallback()` -- browser memanggilnya ketika elemen `<time-formatted>` ditambahkan ke laman (atau saat HTML parser mendeteksinya), dan elemen ini menggunakan [Intl.DateTimeFormat](mdn:/JavaScript/Reference/Global_Objects/DateTimeFormat) bawaan, pemformat data, yang didukung dengan baik di seluruh browser, untuk menampilkan waktu yang diformat dengan baik.\n2. Kita perlu mendaftarkan elemen baru kita dengan `customElements.define (tag, class)`.\n3. Dan kemudian kita bisa menggunakannya di mana saja.\n\n\n```smart header=\"Custom elements upgrade\"\nJika browser menemukan elemen `<time-formatted>` sebelum `customElements.define`, itu bukanlah sebuah kesalahan. Tetapi elemennya belum diketahui, sama seperti tag non-standar lainnya.\n\nseperti elemen \"undefined\" yang bisa diberi style menggunakan CSS selector `:not(:defined)`.\n\nSaat `customElement.define` dipanggil, mereka \"ditingkatkan\": sebuah instance baru dari` TimeFormatted`\nyang dibuat untuk masing-masing pemanggilan, dan `connectedCallback` dipanggil. Mereka menjadi `:defined`.\n\nUntuk mendapatkan informasi tentang elemen kustom, ada methods:\n- `customElements.get(name)` -- mengembalikan kelas untuk elemen khusus dengan `name` yang diberikan,\n- `customElements.whenDefined(name)` -- mengembalikan sebuah promise yang resolves (tanpa value) saat elemen kustom dengan `name` menjadi defined.\n```\n\n```smart header=\"Rendering in `connectedCallback`, not in `constructor`\"\nDalam contoh di atas, konten elemen dirender (dibuat) di `connectedCallback`.\n\nMengapa tidak di `constructor`?\n\nAlasannya sederhana: ketika `constructor` dipanggil, ini terlalu awal. Elemen dibuat, tetapi browser belum memproses/menetapkan atribut pada tahap ini: panggilan ke `getAttribute` akan menghasilkan` null`. Jadi kita tidak bisa merender di sana.\n\nSelain itu, jika anda memikirkannya, itu lebih baik dari segi kinerja - menunda pekerjaan sampai benar-benar dibutuhkan.\n\n`ConnectedCallback` terpicu saat elemen ditambahkan ke dokumen. Tidak hanya ditambahkan ke elemen lain sebagai anak, tetapi sebenarnya menjadi bagian dari halaman. Jadi kita bisa membangun DOM yang terpisah, membuat elemen dan mempersiapkannya untuk digunakan nanti. Mereka hanya akan benar-benar dirender saat berhasil masuk ke halaman.\n```\n\n## Mengamati atribut\n\nDalam implementasi `<time-formatted>` saat ini, setelah elemen dirender, perubahan atribut lebih lanjut tidak berpengaruh apa pun. Itu aneh untuk sebuah elemen HTML. Biasanya, ketika kita mengubah atribut, seperti ʻa.href`, kita mengharapkan perubahan itu segera terlihat. Jadi mari kita perbaiki ini.\n\nKita bisa mengamati atribut dengan memberikan daftarnya di static getter ʻobservedAttributes () `. Untuk atribut seperti, `attributeChangedCallback` dipanggil saat atribut-atributnya diubah. Ini tidak memicu atribut karena alasan kinerja.\n\nIni adalah `<time-formatted>` baru, yang diperbarui secara otomatis ketika atribut berubah:\n\n```html run autorun=\"no-epub\" height=50\n<script>\nclass TimeFormatted extends HTMLElement {\n\n*!*\n  render() { // (1)\n*/!*\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n*!*\n  connectedCallback() { // (2)\n*/!*\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n*!*\n  static get observedAttributes() { // (3)\n*/!*\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n*!*\n  attributeChangedCallback(name, oldValue, newValue) { // (4)\n*/!*\n    this.render();\n  }\n\n}\n\ncustomElements.define(\"time-formatted\", TimeFormatted);\n</script>\n\n<time-formatted id=\"elem\" hour=\"numeric\" minute=\"numeric\" second=\"numeric\"></time-formatted>\n\n<script>\n*!*\nsetInterval(() => elem.setAttribute('datetime', new Date()), 1000); // (5)\n*/!*\n</script>\n```\n\n1. Logika rendering dipindahkan ke *method* helper `render()`.\n2. Kita memanggilnya sekali ketika elemen dimasukkan ke dalam laman.\n3. Untuk perubahan sebuah atribut, cantumkan dalam pemicu `observedAttributes()`, `attributeChangedCallback`.\n4. ...dan merender ulang elemen tersebut.\n5. Pada akhirnya, kita dapat dengan mudah membuat sebuah pengatur waktu langsung.\n\n## Urutan Rendering\n\nKetika HTML parser membangun DOM, elemen diproses satu demi satu, Induk sebelum anak. Misalnya. jika kita memiliki `<outer> <inner> </inner> </outer>`, maka elemen `<outer>` dibuat dan dihubungkan ke DOM terlebih dahulu, kemudian `<inner>`.\n\nItu mengarah pada konsekuensi penting untuk elemen kustom.\n\nMisalnya, jika elemen khusus mencoba mengakses `innerHTML` di `connectedCallback`, elemen tersebut tidak mendapat apa-apa:\n\n```html run height=40\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n\n  connectedCallback() {\n*!*\n    alert(this.innerHTML); // empty (*)\n*/!*\n  }\n\n});\n</script>\n\n*!*\n<user-info>John</user-info>\n*/!*\n```\n\nJika anda menjalankannya, `alert` kosong.\n\nItu persis terjadi karena tidak ada anak pada tahap itu, DOM belum selesai. HTML parser menghubungkan elemen kustom `<user-info>`, dan akan melanjutkan ke anaknya, tetapi belum melakukannya.\n\nJika kita ingin meneruskan informasi ke elemen kustom, kita dapat menggunakan atribut. Atribut-atribut tersebut langsung tersedia.\n\nAtau, jika kita benar-benar membutuhkan anak, kita dapat menunda akses ke mereka dengan `setTimeout` tanpa penundaan.\n\nIni bekerja :\n\n```html run height=40\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n\n  connectedCallback() {\n*!*\n    setTimeout(() => alert(this.innerHTML)); // John (*)\n*/!*\n  }\n\n});\n</script>\n\n*!*\n<user-info>John</user-info>\n*/!*\n```\n\nSekarang `alert` pada baris `(*)` menunjukkan \"John\" saat kita menjalankannya secara asinkron setelah HTML parser selesai. Kita dapat memproses anaknya jika diperlukan dan menyelesaikan inisialisasi.\n\nDi sisi lain, solusi ini juga belum sempurna. Jika elemen kustom bersarang juga menggunakan `setTimeout` untuk menginisialisasi dirinya sendiri, maka elemen tersebut akan dimasukkan ke dalam antrean:` setTimeout` terluar dipicu terlebih dahulu, kemudian bagian dalamnya.\n\nJadi elemen outer menyelesaikan inisialisasinya sebelum elemen inner.\n\nMari kita tunjukkan itu pada contoh:\n\n```html run height=0\n<script>\ncustomElements.define('user-info', class extends HTMLElement {\n  connectedCallback() {\n    alert(`${this.id} connected.`);\n    setTimeout(() => alert(`${this.id} initialized.`));\n  }\n});\n</script>\n\n*!*\n<user-info id=\"outer\">\n  <user-info id=\"inner\"></user-info>\n</user-info>\n*/!*\n```\n\nUrutan keluaran:\n\n1. outer terhubung.\n2. inner terhubung.\n3. outer diinisialisasi.\n4. inner diinisialisasi.\n\nKita dapat melihat dengan jelas bahwa elemen outer menyelesaikan inisialisasi `(3)` sebelum elemen inner `(4)`.\n\nTidak ada *callback* bawaan yang terpicu setelah elemen bersarang siap. Jika perlu, kita bisa menerapkannya sendiri. Misalnya, elemen inner bisa mengirimkan *events* seperti `initialized`, dan elemen outer bisa mendengarkan dan bereaksi padanya.\n\n## Customized built-in elements\n\nElemen baru yang kita buat, seperti `<time-formatted>`, tidak memiliki semantik terkait. Mereka tidak dikenal oleh mesin pencari dan perangkat aksesibilitas tidak dapat menanganinya.\n\nTapi hal seperti itu bisa jadi penting. Misalnya, mesin telusur akan tertarik untuk mengetahui bahwa kita benar-benar menunjukkan waktu. Dan jika kita membuat sebuah tombol khusus, mengapa tidak menggunakan kembali fungsionalitas `<button>` yang ada?\n\nKita dapat meng-*extend* dan menyesuaikan elemen bawaan HTML dengan mewarisi dari kelasnya.\n\nMisalnya, tombol adalah *instances* dari `HTMLButtonElement`, mari kita buat di atasnya.\n\n1. Extend `HTMLButtonElement` dengan kelas kita:\n\n    ```js\n    class HelloButton extends HTMLButtonElement { /* custom element methods */ }\n    ```\n\n2. Berikan argumen ketiga untuk `customElements.define`, yang menetapkan tag:\n    ```js\n    customElements.define('hello-button', HelloButton, *!*{extends: 'button'}*/!*);\n    ```    \n\n    Mungkin ada tag berbeda yang berbagi kelas DOM yang sama, itulah mengapa menetapkan `extends` diperlukan.\n\n3. Pada akhirnya, untuk menggunakan elemen kustom kita, masukkan tag `<button>` biasa, tetapi tambahkan `is = \"hello-button\" `ke dalamnya:\n    ```html\n    <button is=\"hello-button\">...</button>\n    ```\n\nBerikut contoh lengkapnya:\n\n```html run autorun=\"no-epub\"\n<script>\n// The button that says \"hello\" on click\nclass HelloButton extends HTMLButtonElement {\n*!*\n  constructor() {\n*/!*\n    super();\n    this.addEventListener('click', () => alert(\"Hello!\"));\n  }\n}\n\n*!*\ncustomElements.define('hello-button', HelloButton, {extends: 'button'});\n*/!*\n</script>\n\n*!*\n<button is=\"hello-button\">Click me</button>\n*/!*\n\n*!*\n<button is=\"hello-button\" disabled>Disabled</button>\n*/!*\n```\n\nTombol baru kita meng-*extends* tombol bawaan. Jadi, style dan fitur standar tetap sama seperti atribut `disabled`.\n\n## Referensi\n\n- HTML Living Standard: <https://html.spec.whatwg.org/#custom-elements>.\n- Compatiblity: <https://caniuse.com/#feat=custom-elementsv1>.\n\n## Ringkasan\n\nElemen kustom dapat terdiri dari dua jenis:\n\n1. \"Autonomous\" -- tag baru, *extending* `HTMLElement`.\n\n    Skema definisi:\n\n    ```js\n    class MyElement extends HTMLElement {\n      constructor() { super(); /* ... */ }\n      connectedCallback() { /* ... */ }\n      disconnectedCallback() { /* ... */  }\n      static get observedAttributes() { return [/* ... */]; }\n      attributeChangedCallback(name, oldValue, newValue) { /* ... */ }\n      adoptedCallback() { /* ... */ }\n     }\n    customElements.define('my-element', MyElement);\n    /* <my-element> */\n    ```\n\n2. \"Customized built-in elements\" -- ekstensi dari elemen yang ada.\n\n    Membutuhkan satu lagi argumen `.define`, dan `is =\" ... \"` dalam HTML:\n    ```js\n    class MyButton extends HTMLButtonElement { /*...*/ }\n    customElements.define('my-button', MyElement, {extends: 'button'});\n    /* <button is=\"my-button\"> */\n    ```\n\nElemen kustom didukung dengan baik di antara browser. Edge agak tertinggal, tetapi ada *polyfill*\n<https://github.com/webcomponents/polyfills/tree/master/packages/webcomponentsjs>.\n"
  },
  {
    "path": "8-web-components/2-custom-elements/head.html",
    "content": "<script>\n  /*\nclass TimeFormatted extends HTMLElement {\n\n  render() {\n    let date = new Date(this.getAttribute('datetime') || Date.now());\n\n    this.innerHTML = new Intl.DateTimeFormat(\"default\", {\n      year: this.getAttribute('year') || undefined,\n      month: this.getAttribute('month') || undefined,\n      day: this.getAttribute('day') || undefined,\n      hour: this.getAttribute('hour') || undefined,\n      minute: this.getAttribute('minute') || undefined,\n      second: this.getAttribute('second') || undefined,\n      timeZoneName: this.getAttribute('time-zone-name') || undefined,\n    }).format(date);\n  }\n\n  connectedCallback() { // (2)\n    if (!this.rendered) {\n      this.render();\n      this.rendered = true;\n    }\n  }\n\n  static get observedAttributes() { // (3)\n    return ['datetime', 'year', 'month', 'day', 'hour', 'minute', 'second', 'time-zone-name'];\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) { // (4)\n    this.render();\n  }\n\n}\n\nwindow.customElements && customElements.define(\"time-formatted\", TimeFormatted);\n*/\n</script>\n"
  },
  {
    "path": "8-web-components/3-shadow-dom/article.md",
    "content": "# Shadow DOM\n\nShadow DOM berfungsi untuk enkapsulasi. Ia memungkinkan sebuah komponen memiliki pohon DOM \"bayangan\" sendiri, yang tidak dapat diakses secara tidak sengaja dari dokumen utama, memungkinkan untuk memiliki aturan gaya secara lokal, dan banyak lagi.\n\n## Built-in shadow DOM\n\nPernahkah anda berpikir betapa rumitnya kontrol browser dibuat dan ditata?\n\nSeperti `<input type=\"range\">`:\n\n<p>\n<input type=\"range\">\n</p>\n\nBrowser menggunakan DOM / CSS secara internal untuk menggambarnya. Struktur DOM itu biasanya tersembunyi dari kita, tetapi kita dapat melihatnya di alat pengembang. Misalnya, di Chrome, kita perlu mengaktifkan di Dev Tools opsi \"Show user agent shadow DOM\".\n\nKemudian `<input type =\"range\">` terlihat seperti ini:\n\n![](shadow-dom-range.png)\n\nApa yang anda lihat di bawah `#shadow-root` disebut \"shadow DOM\".\n\nKita tidak bisa mendapatkan elemen shadow DOM bawaan dengan panggilan atau *selectors* JavaScript biasa. Ini bukan anak biasa, tetapi teknik enkapsulasi yang kuat.\n\nDalam contoh di atas, kita dapat melihat atribut yang berguna yaitu `pseudo`. Ini non-standar, ada karena alasan historis. Kita dapat menggunakannya untuk memberi gaya pada subelemen dengan CSS, seperti ini:\n\n```html run autorun\n<style>\n/* make the slider track red */\ninput::-webkit-slider-runnable-track {\n  background: red;\n}\n</style>\n\n<input type=\"range\">\n```\n\nSekali lagi, `pseudo` adalah atribut non-standar. Secara kronologis, browser pertama kali mulai bereksperimen dengan struktur DOM internal untuk mengimplementasikan kontrol, dan kemudian, setelah beberapa waktu, shadow DOM distandarisasi untuk memungkinkan kami, pengembang, melakukan hal serupa.\n\nSelanjutnya, kita akan menggunakan standar shadow DOM modern, yang dicakup oleh [DOM Spec](https://dom.spec.whatwg.org/#shadow-trees) terkait spesifikasi lainnya.\n\n## Shadow tree\n\nElemen DOM dapat memiliki dua jenis subpohon DOM:\n\n1. Light tree -- subpohon DOM biasa, terbuat dari anak-anak HTML. Semua subpohon yang telah kita lihat di bab sebelumnya adalah termasuk \"light\".\n2. Shadow tree -- subpohon DOM tersembunyi, tidak tercetak dalam HTML, tersembunyi dari mata-mata.\n\nJika sebuah elemen memiliki keduanya, browser hanya akan merender shadow tree. Tapi kita bisa mengatur semacam komposisi antara shadow dan light tree juga. Kita akan melihat detailnya nanti di bab <info:slots-composition>.\n\nShadow tree dapat digunakan di Elemen Kustom untuk menyembunyikan internal komponen dan menerapkan gaya lokal-komponen.\n\nMisalnya, elemen `<show-hello>` ini menyembunyikan DOM internalnya di pohon bayangan:\n\n```html run autorun height=60\n<script>\ncustomElements.define('show-hello', class extends HTMLElement {\n  connectedCallback() {\n    const shadow = this.attachShadow({mode: 'open'});\n    shadow.innerHTML = `<p>\n      Hello, ${this.getAttribute('name')}\n    </p>`;\n  }  \n});\n</script>\n\n<show-hello name=\"John\"></show-hello>\n```\n\nBegitulah tampilan DOM yang dihasilkan di alat pengembang Chrome, semua konten berada di bawah \"#shadow-root\":\n\n![](shadow-dom-say-hello.png)\n\nPertama, panggilan ke `elem.attachShadow ({mode:…})` adalah untuk membuat pohon bayangan.\n\nAda dua batasan:\n1. Kita hanya dapat membuat satu shadow root per elemen.\n2. `Elem` harus berupa elemen kustom, atau salah satu dari: \"article\", \"aside\", \"blockquote\", \"body\", \"div\", \"footer\", \"h1..h6\", \"header\", \"main\" \"nav\", \"p\", \"section\", atau \"span\". Elemen lain, seperti `<img>`, tidak dapat menjadi host pohon bayangan.\n\nOpsi `mode` mengatur tingkat enkapsulasi. `mode` harus memiliki salah satu dari dua nilai:\n- `\"open\"` -- *shadow root* tersedia sebagai `elem.shadowRoot`.\n\n    Kode apa pun dapat mengakses pohon bayangan dari `elem`.  \n- `\"closed\"` -- `elem.shadowRoot` selalu bernilai `null`.\n\n    Kita hanya bisa mengakses shadow DOM dengan referensi yang dikembalikan oleh `attachShadow` (dan mungkin tersembunyi di dalam kelas). Shadow tree asli browser, seperti `<input type =\" range \">`, ditutup. Tidak ada cara untuk mengaksesnya.\n\n[Shadow root](https://dom.spec.whatwg.org/#shadowroot), dikembalikan oleh `attachShadow`, seperti sebuah elemen: kita bisa menggunakan metode `innerHTML` atau DOM, seperti `append`, untuk mengisinya.\n\nElemen dengan sebuah shadow root disebut \"shadow tree host\", dan tersedia sebagai properti `host` root bayangan:\n\n```js\n// dengan asumsi {mode: \"open\"}, jika tidak nilai elem.shadowRoot adalah null\nalert(elem.shadowRoot.host === elem); // true\n```\n\n## Enkapsulasi\n\nShadow DOM sangat dibatasi dari dokumen utama:\n\n1. Elemen shadow DOM tidak terlihat oleh `querySelector` dari light DOM. Secara khusus, elemen Shadow DOM mungkin memiliki ID yang bertentangan dengan ID yang ada di light DOM. Mereka harus unik hanya di dalam shadow tree.\n2. Shadow DOM memiliki stylesheet sendiri. Aturan gaya dari DOM luar tidak diterapkan.\n\nSebagai contoh:\n\n```html run untrusted height=40\n<style>\n*!*\n  /* document style won't apply to the shadow tree inside #elem (1) */\n*/!*\n  p { color: red; }\n</style>\n\n<div id=\"elem\"></div>\n\n<script>\n  elem.attachShadow({mode: 'open'});\n*!*\n    // shadow tree memiliki gaya tersendiri (2)\n*/!*\n  elem.shadowRoot.innerHTML = `\n    <style> p { font-weight: bold; } </style>\n    <p>Hello, John!</p>\n  `;\n\n*!*\n  // <p> hanya terlihat dari kueri di dalam shadow tree (3)\n*/!*\n  alert(document.querySelectorAll('p').length); // 0\n  alert(elem.shadowRoot.querySelectorAll('p').length); // 1\n</script>  \n```\n\n1. Gaya dari dokumen tidak mempengaruhi shadow tree.\n2. ...Tapi gaya dari dalam berpengaruh.\n3. Untuk mendapatkan elemen di shadow tree, kita harus melakukan kueri dari dalam tree.\n\n## Referensi\n\n- DOM: <https://dom.spec.whatwg.org/#shadow-trees>\n- Compatibility: <https://caniuse.com/#feat=shadowdomv1>\n- Shadow DOM disebutkan dalam banyak spesifikasi lainnya, misalnya [DOM Parsing](https://w3c.github.io/DOM-Parsing/#the-innerhtml-mixin) menentukan bahwa shadow root memiliki `innerHTML`.\n\n\n## Ringkasan\n\nShadow DOM adalah cara untuk membuat DOM komponen-lokal.\n\n1. `shadowRoot = elem.attachShadow({mode: open|closed})` -- membuat shadow DOM untuk `elem`. Jika `mode=\"open\"`, maka itu dapat diakses sebagai properti `elem.shadowRoot`.\n2. Kita bisa mengisi `shadowRoot` menggunakan `innerHTML` atau metode DOM lainnya.\n\nElemen shadow DOM:\n- Memiliki id-nya sendiri,\n- Tidak terlihat oleh selectors JavaScript dari dokumen utama, seperti `querySelector`,\n- Gunakan gaya hanya dari shadow tree, bukan dari dokumen utama.\n\nShadow DOM, jika ada, akan dirender oleh browser alih-alih yang disebut \"light DOM\" (turunan biasa). Dalam bab <info:slots-composition> kita akan melihat bagaimana menyusunnya.\n"
  },
  {
    "path": "8-web-components/4-template-element/article.md",
    "content": "\n# Elemen template\n\nElemen `<template>` bawaan berfungsi sebagai penyimpanan untuk template markup HTML. Browser mengabaikan isinya, hanya memeriksa validitas sintaks, tetapi kita dapat mengakses dan menggunakannya dalam JavaScript, untuk membuat elemen lain.\n\nSecara teori, kita bisa membuat elemen tak terlihat di suatu tempat di HTML untuk tujuan penyimpanan markup HTML. Apa yang spesial dari `<template>`?\n\nPertama, isinya dapat berupa HTML yang valid, meskipun biasanya membutuhkan tag penutup yang tepat.\n\nMisalnya, kita dapat meletakkan baris tabel `<tr>` di dalamnya:\n```html\n<template>\n  <tr>\n    <td>Contents</td>\n  </tr>\n</template>\n```\n\nBiasanya, jika kita mencoba untuk meletakkan `<tr>` di dalam, katakanlah, sebuah `<div>`, browser mendeteksi struktur DOM yang tidak valid dan \"memperbaikinya\", menambahkan `<table>` di sekitarnya. Bukan itu yang kita inginkan. Di sisi lain, `<template>` menyimpan persis apa yang kita tempatkan di sana.\n\nKita juga bisa menempatkan gaya dan skrip ke dalam `<template>`:\n\n```html\n<template>\n  <style>\n    p { font-weight: bold; }\n  </style>\n  <script>\n    alert(\"Hello\");\n  </script>\n</template>\n```\n\nBrowser menganggap konten `<template>` berada \"di luar dokumen\": gaya tidak diterapkan, skrip tidak dijalankan, `<video autoplay>` tidak dijalankan, dll.\n\nKonten menjadi hidup (gaya css diterapkan, skrip dijalankan, dll.) Ketika kita memasukkannya ke dalam dokumen.\n\n## Memasukkan template\n\nKonten template tersedia dalam properti `content` sebagai [DocumentFragment](info:modifying-document#document-fragment) -- yaitu jenis khusus dari simpul DOM.\n\nKita bisa memperlakukannya sebagai simpul DOM lainnya, kecuali satu properti khusus: ketika kita memasukkannya ke suatu tempat, anak-anaknya akan dimasukkan.\n\nSebagai contoh:\n\n```html run\n<template id=\"tmpl\">\n  <script>\n    alert(\"Hello\");\n  </script>\n  <div class=\"message\">Hello, world!</div>\n</template>\n\n<script>\n  let elem = document.createElement('div');\n\n*!*\n  // Menggandakan konten template untuk digunakan kembali beberapa kali\n  elem.append(tmpl.content.cloneNode(true));\n*/!*\n\n  document.body.append(elem);\n  // Sekarang skrip dari <template> berjalan\n</script>\n```\n\nMari kita tulis ulang contoh Shadow DOM dari bab sebelumnya menggunakan `<template>`:\n\n```html run untrusted autorun=\"no-epub\" height=60\n<template id=\"tmpl\">\n  <style> p { font-weight: bold; } </style>\n  <p id=\"message\"></p>\n</template>\n\n<div id=\"elem\">Click me</div>\n\n<script>\n  elem.onclick = function() {\n    elem.attachShadow({mode: 'open'});\n\n*!*\n    elem.shadowRoot.append(tmpl.content.cloneNode(true)); // (*)\n*/!*\n\n    elem.shadowRoot.getElementById('message').innerHTML = \"Hello from the shadows!\";\n  };\n</script>\n```\n\nDi baris `(*)` saat kita menggandakan dan memasukkan `tmpl.content`, sebagai ` DocumentFragment`-nya, turunannya (`<style>`, `<p>`) akan disisipkan.\n\nMereka membentuk shadow DOM:\n\n```html\n<div id=\"elem\">\n  #shadow-root\n    <style> p { font-weight: bold; } </style>\n    <p id=\"message\"></p>\n</div>\n```\n\n## Ringkasan\n\nUntuk meringkas:\n\n- `<template>` konten dapat berupa HTML yang benar secara sintaksis.\n- `<template>` konten dianggap \"di luar dokumen\", jadi tidak memengaruhi apa pun.\n- Kita dapat mengakses `template.content` dari JavaScript, mengkloningnya untuk digunakan kembali dalam komponen baru.\n\nTag `<template>` cukup unik, karena:\n\n- Browser memeriksa sintaks HTML di dalamnya (sebagai lawan menggunakan string template di dalam skrip).\n- ...Namun tetap mengizinkan penggunaan tag HTML tingkat atas, bahkan yang tidak masuk akal tanpa pembungkus yang tepat (mis. `<tr>`).\n- Konten menjadi interaktif: skrip dijalankan, `<video autoplay>` memutar dll, ketika dimasukkan ke dalam dokumen.\n\nElemen `<template>` tidak menampilkan mekanisme iterasi, pengikatan data, atau substitusi variabel, tetapi kita dapat mengimplementasikannya di atasnya.\n"
  },
  {
    "path": "8-web-components/5-slots-composition/article.md",
    "content": "#  Slot shadow DOM, komposisi\n\nBanyak jenis komponen seperti tab, menu, galeri gambar, dan sebagainya, memerlukan konten untuk di-*render*\n\nSama seperti `<select>` bawaan peramban mengharapkan item `<option>`, `<custom-tabs>` kita juga mungkin mengharapkan konten tab yang sebenarnya untuk diteruskan. Dan sebuah `<custom-menu>` mungkin mengharapkan item menu.\n\nKode untuk membuat `<custom-menu>` dapat terlihat seperti berikut:\n```html\n<custom-menu>\n\t<title>Candy menu</title>\n\t<item>Lollipop</item>\n\t<item>Fruit Toast</item>\n\t<item>Cup Cake</item>\n</custom-menu>\n```\n...Maka komponen kita harus me-*render*-nya dengan benar, sebagai menu yang bagus dengan judul dan item yang diberikan, menangani *events*  menu, dll.\n\nBagaimana mengimplementasikannya?\n\nKita dapat mencoba menganalisis konten elemen dan secara dinamis menyalin-mengatur ulang node DOM. Itu memungkinkan, tetapi jika kita memindahkan elemen ke shadow DOM, maka gaya CSS dari dokumen tidak berlaku di sana, sehingga gaya visual mungkin hilang. Juga itu membutuhkan beberapa pengkodean.\n\nUntungnya, kita tidak perlu melakukannya. Shadow DOM mendukung elemen `<slot>`, yang secara otomatis diisi oleh konten dari light DOM.\n\n##  Slot bernama\n\nLet's see how slots work on a simple example.\n\nHere, `<user-card>` shadow DOM provides two slots, filled from light DOM:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <div>Name:\n*!*\n        <slot name=\"username\"></slot>\n*/!*\n      </div>\n      <div>Birthday:\n*!*\n        <slot name=\"birthday\"></slot>\n*/!*\n      </div>\n    `;\n  }\n});\n</script>\n\n<user-card>\n  <span *!*slot=\"username\"*/!*>John Smith</span>\n  <span *!*slot=\"birthday\"*/!*>01.01.2001</span>\n</user-card>\n\n```\n\nDalam shadow DOM, `<slot name=\"X\">` mendefinisikan sebuah \"titik penyisipan\", sebuah tempat dimana elemen dengan `slot=\"X\"` di-*render*.\n\nKemudian browser melakukan \"komposisi\": mengambil elemen dari light DOM dan me-*render*-nya di slot shadow DOM yang sesuai. Pada akhirnya, kita memiliki apa yang kita inginkan -- sebuah komponen yang dapat diisi dengan data.\n\nIni adalah struktur DOM setelah script, tidak memperhitungkan komposisi:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\"></slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n</user-card>\n```\n\nKita membuat shadow DOM, seperti ini, dibawah `#shadow-root`. Sekarang elemen memiliki light dan shadow DOM.\n\nUntuk tujuan *rendering*, untuk setiap `<slot name=\"...\">` di dalam shadow DOM, peramban mencari `slot=\"...\"` dengan nama yang sama di light DOM. Elemen-elemen ini ditampilkan di dalam slot:\n\n![](shadow-dom-user-card.svg)\n\nHasilnya disebut \"flattened\" DOM:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <!-- slotted element is inserted into the slot -->\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\">\n        <span slot=\"birthday\">01.01.2001</span>\n      </slot>\n    </div>\n</user-card>\n```\n\n...Tetapi flattened DOM hanya ada untuk tujuan *rendering* dan *event-handling*. Ini semacam \"virtual\". Begitulah cara slot elemen ditampilkan. Tetapi *nodes* dalam dokumen sebenarnya tidak dipindahkan!\n\nItu dapat dengan mudah diperiksa jika kita menjalankan `querySelectorAll`: *nodes* masih di tempatnya.\n\n```js\n// light DOM <span> nodes masih di tempat yang sama, di bawah `<user-card>`\nalert( document.querySelectorAll('user-card span').length ); // 2\n```\n\nJadi, flattened DOM diturunkan dari shadow DOM dengan menyisipkan slots, Peramban me-*render*-nya dan menggunakannya untuk pewarisan gaya, event propagation (lebih lanjut tentang itu nanti). Tetapi JavaScript masih melihat dokumen \"sebagaimana adanya\", sebelum *flattening*.\n\n````warn header=\"Only top-level children may have slot=\\\"...\\\" attribute\"\nAtribut `slot=\"...\"` hanya valid untuk direct children (anak langsung) dari shadow host (dalam contoh kita, elemen `<user-card>`). Untuk elemen bersarang diabaikan.\n\nSebagai contoh, `<span>` kedua disini diabaikan (karena ini bukan top-level child dari `<user-card>`):\n```html\n<user-card>\n  <span slot=\"username\">John Smith</span>\n  <div>\n    <!-- slot tidak valid, harus anak langsung dari user-card -->\n    <span slot=\"birthday\">01.01.2001</span>\n  </div>\n</user-card>\n```\n\n````\n\nJika ada beberapa elemen di light DOM dengan nama slot yang sama, mereka ditambahkan ke dalam slot, satu demi satu.\n\nSebagai contoh:\n\n```html\n<user-card>\n  <span slot=\"username\">John</span>\n  <span slot=\"username\">Smith</span>\n</user-card>\n```\n\nMemberikan flattened DOM ini dengan dua elemen di `<slot name=\"username\">`:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <span slot=\"username\">John</span>\n        <span slot=\"username\">Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n</user-card>\n```\n\n##  Konten pengganti slot\n\nJika kita memasukkan sesuatu ke dalam sebuah `<slot>`, itu akan menjadi pengganti konten bawaan. Peramban menampilkannya jika tidak ada pengisi yang sesuai di light DOM.\n\nSebagai contoh, di dalam bagian shadow DOM ini,  `Anonymous` di-*render* jika tidak ada `slot=\"username\"` di light DOM.\n\n```html\n<div>Name:\n  <slot name=\"username\">Anonymous</slot>\n</div>\n```\n\n##  Slot default: slot pertama yang tidak bernama\n\n`<slot>` pertama di shadow DOM yang tidak memiliki nama adalah slot \"default\". slot default ini mendapatkan semua node dari light DOM yang tidak ditempatkan di tempat lain. \n\nSebagai contoh, mari tambahkan slot default pada `<user-card>` yang menampilkan semua informasi tanpa slot tentang pengguna:\n\n```html run autorun=\"no-epub\" untrusted height=140\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n    <div>Name:\n      <slot name=\"username\"></slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\"></slot>\n    </div>\n    <fieldset>\n      <legend>Other information</legend>\n*!*\n      <slot></slot>\n*/!*\n    </fieldset>\n    `;\n  }\n});\n</script>\n\n<user-card>\n*!*\n  <div>I like to swim.</div>\n*/!*\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n*!*\n  <div>...And play volleyball too!</div>\n*/!*\n</user-card>\n```\n\nSemua konten light DOM yang tidak memiliki slot masuk ke dalam *fieldset* \"Other information\".\n\nElements are appended to a slot one after another, so both unslotted pieces of information are in the default slot together.\n\nElemen ditambahkan ke slot satu demi satu, sehingga kedua potongan informasi yang tidak memiliki slot berada di slot default bersama-sama.\n\nDOM yang diratakan terlihat seperti ini:\n\n```html\n<user-card>\n  #shadow-root\n    <div>Name:\n      <slot name=\"username\">\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n    <div>Birthday:\n      <slot name=\"birthday\">\n        <span slot=\"birthday\">01.01.2001</span>\n      </slot>\n    </div>\n    <fieldset>\n      <legend>Other information</legend>\n*!*\n      <slot>\n        <div>I like to swim.</div>\n        <div>...And play volleyball too!</div>\n      </slot>\n*/!*\n    </fieldset>\n</user-card>\n```\n\n##  Contoh: menu\n\nSekarang mari kembali ke `<custom-menu>`, yang disebutkan di awal bab.\n\nKita dapat menggunakan slot untuk mendistribusikan elemen\n\nBerikut *markup* untuk `<custom-menu>`:\n\n```html\n<custom-menu>\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n  <li slot=\"item\">Cup Cake</li>\n</custom-menu>\n```\n\nTemplate shadow DOM dengan slot yang tepat:\n\n```html\n<template id=\"tmpl\">\n  <style> /* menu styles */ </style>\n  <div class=\"menu\">\n    <slot name=\"title\"></slot>\n    <ul><slot name=\"item\"></slot></ul>\n  </div>\n</template>\n```\n\n1. `<span slot=\"title\">` masuk ke `<slot name=\"title\">`.\n\n2. Ada banyak `<li slot=\"item\">` di template, tetapi hanya satu `<slot name=\"item\">` di template. Jadi semua `<li slot=\"item\">` tersebut ditambahkan ke `<slot name=\"item\">` satu demi satu, sehingga membentuk *list*.\n\nDOM yang diratakan menjadi:\n\n```html\n<custom-menu>\n  #shadow-root\n    <style> /* menu styles */ </style>\n    <div class=\"menu\">\n      <slot name=\"title\">\n        <span slot=\"title\">Candy menu</span>\n      </slot>\n      <ul>\n        <slot name=\"item\">\n          <li slot=\"item\">Lollipop</li>\n          <li slot=\"item\">Fruit Toast</li>\n          <li slot=\"item\">Cup Cake</li>\n        </slot>\n      </ul>\n    </div>\n</custom-menu>\n```\n\nMungkin ada yang memperhatikan bahwa, dalam DOM yang valid, `<li>` harus merupakan *direct child* (anak langsung) dari `<ul>`. Tetapi itu adalah DOM yang diratakan, ini menjelaskan bagaimana komponen di-*render*, hal seperti itu terjadi secara alami di sini.\n\nKita hanya perlu menambahkan *handler* `click` untuk membuka/menutup *list*, dan `<custom-menu>` sudah siap:\n\n```js\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n\n    // tmpl is the shadow DOM template (above)\n    this.shadowRoot.append( tmpl.content.cloneNode(true) );\n\n    // we can't select light DOM nodes, so let's handle clicks on the slot\n    this.shadowRoot.querySelector('slot[name=\"title\"]').onclick = () => {\n      // open/close the menu\n      this.shadowRoot.querySelector('.menu').classList.toggle('closed');\n    };\n  }\n});\n```\n\nBerikut adalah demo lengkapnya:\n\n[iframe src=\"menu\" height=140 edit]\n\nTentu saja, kita dapat menambahkan lebih banyak fungsionalitas ke dalamnya: *events*, *method*, dan sebagainya.\n\n##  Memperbarui slot\n\nBagaimana jika kode luar ingin menambah/menghapus item menu secara dinamis?\n\n**Browser memantau slot dan memperbarui *rendering* jika elemen slot ditambahkan/dihapus.**\n\nJuga, karena node light DOM tidak disalin, tetapi hanya di-*render* di dalam slot, perubahan di dalamnya segera terlihat.\n\nJadi kita tidak perlu melakukan apapun untuk memperbarui *rendering*. Namun jika kode komponen ingin mengetahui tentang perubahan slot, maka tersedia *event* `slotchange`.\n\nMisalnya, di sini item menu dimasukkan secara dinamis setelah 1 detik, dan *title* berubah setelah 2 detik:\n\n```html run untrusted height=80\n<custom-menu id=\"menu\">\n  <span slot=\"title\">Candy menu</span>\n</custom-menu>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div class=\"menu\">\n      <slot name=\"title\"></slot>\n      <ul><slot name=\"item\"></slot></ul>\n    </div>`;\n\n    // shadowRoot can't have event handlers, so using the first child\n    this.shadowRoot.firstElementChild.addEventListener('slotchange',\n      e => alert(\"slotchange: \" + e.target.name)\n    );\n  }\n});\n\nsetTimeout(() => {\n  menu.insertAdjacentHTML('beforeEnd', '<li slot=\"item\">Lollipop</li>')\n}, 1000);\n\nsetTimeout(() => {\n  menu.querySelector('[slot=\"title\"]').innerHTML = \"New menu\";\n}, 2000);\n</script>\n```\n\n*Render* menu diperbarui setiap kali tanpa campur tangan kita.\n\nAda dua *events* `slotchange` di sini:\n\n1. Saat inisialisasi:\n\n`slotchange: title` langsung terpicu, saat `slot=\"title\"` dari light DOM masuk ke slot yang sesuai.\n\n2. Setelah 1 detik:\n\n`slotchange: item` terpicu, saat `<li slot=\"item\">` baru ditambahkan.\n\nPlease note: there's no `slotchange` event after 2 seconds, when the content of `slot=\"title\"` is modified. That's because there's no slot change. We modify the content inside the slotted element, that's another thing.\n\nHarap diperhatikan: tidak ada *event* `slotchange` setelah 2 detik, ketika konten `slot=\"title\"` diubah. Itu karena tidak ada perubahan slot. Kita memodifikasi konten di dalam elemen yang ber-slot, itu adalah hal lain.\n\nJika kita ingin melacak modifikasi internal light DOM dari JavaScript, itu juga dimungkinkan menggunakan mekanisme yang lebih umum: [MutationObserver](info:mutation-observer).\n\n##  Slot API\n\nTerakhir, mari kita bahas *method* JavaScript terkait slot.\n\nSeperti yang telah kita lihat sebelumnya, JavaScript melihat DOM \"asli\", tanpa meratakan. Akan tetapi, jika *shadow tree* memiliki `{mode: 'open'}`, maka kita dapat mengetahui elemen mana yang ditetapkan ke slot dan, sebaliknya, slot dengan elemen di dalamnya:\n\n- `node.assignedSlot` -- mengembalikan elemen `<slot>` tempat `node` ditetapkan.\n\n- `slot.assignedNodes({flatten: true/false})` -- Node DOM, ditetapkan ke slot. Opsi `flatten` adalah `false` secara default. Jika secara eksplisit disetel ke `true`, maka akan melihat lebih dalam ke DOM yang diratakan, mengembalikan slot bersarang jika ada komponen bersarang dan konten pengganti jika tidak ada node yang ditetapkan.\n\n- `slot.assignedElements({flatten: true/false})` -- Elemen DOM, ditetapkan ke slot (sama seperti di atas, tetapi hanya elemen node).\n\nMetode ini berguna ketika kita tidak hanya perlu menampilkan konten yang ditempatkan, tetapi juga perlu melacaknya dalam JavaScript.\n\nSebagai contoh, jika komponen `<custom-menu>` ingin mengetahui apa yang ditampilkannya, maka komponen tersebut dapat melacak `slotchange` dan mendapatkan item dari `slot.assignedElements`:\n\n```html run untrusted height=120\n<custom-menu id=\"menu\">\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n</custom-menu>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  items = []\n\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div class=\"menu\">\n      <slot name=\"title\"></slot>\n      <ul><slot name=\"item\"></slot></ul>\n    </div>`;\n\n    // triggers when slot content changes\n*!*\n    this.shadowRoot.firstElementChild.addEventListener('slotchange', e => {\n      let slot = e.target;\n      if (slot.name == 'item') {\n        this.items = slot.assignedElements().map(elem => elem.textContent);\n        alert(\"Items: \" + this.items);\n      }\n    });\n*/!*\n  }\n});\n\n// items update after 1 second\nsetTimeout(() => {\n  menu.insertAdjacentHTML('beforeEnd', '<li slot=\"item\">Cup Cake</li>')\n}, 1000);\n</script>\n```\n\n##  Ringkasan\n\nBiasanya, jika sebuah elemen memiliki shadow DOM, maka light DOM-nya tidak ditampilkan. Slot memungkinkan untuk menampilkan elemen dari light DOM di tempat tertentu dari shadow DOM.\n\nAda dua jenis slot:\n\n- Slot bernama: `<slot name=\"X\">...</slot>` -- mendapatkan *light children* dengan `slot=\"X\"`.\n\n- Slot default: `<slot>` pertama tanpa nama (slot tanpa nama berikutnya diabaikan) -- mendapatkan *light children* yang tidak diberi slot.\n\n- Jika ada banyak elemen untuk slot yang sama -- elemen-elemen tersebut ditambahkan satu demi satu.\n\n- Konten elemen `<slot>` digunakan sebagai pengganti. Ini ditampilkan jika tidak ada *light children* untuk slot.\n\nProses *rendering* elemen yang ber-slot di dalam slot mereka disebut \"komposisi\". Hasilnya disebut \"DOM yang diratakan\".\n\nKomposisi tidak benar-benar memindahkan node, dari sudut pandang JavaScript DOM masih sama.\n\nJavaScript dapat mengakses slot menggunakan *method*:\n\n- `slot.assignedNodes/Elements()` -- mengembalikan node/elemen di dalam `slot`.\n\n- `node.assignedSlot` -- properti terbalik, mengembalikan slot dengan sebuah node.\n\nJika kita ingin mengetahui apa yang kita tampilkan, kita dapat melacak konten slot menggunakan:\n- *event* `slotchange` -- memicu pertama kali saat slot diisi, dan pada setiap operasi tambah/hapus/ganti elemen slot, tetapi bukan anaknya. Slotnya adalah `event.target`.\n\n- [MutationObserver](info:mutation-observer) untuk masuk lebih dalam ke konten slot, memperhatikan perubahan di dalamnya.\n\nSaat ini, seperti yang kita tahu cara untuk menampilkan elemen dari light DOM di shadow DOM, mari kita lihat cara menata gaya dengan benar. Aturan dasarnya adalah elemen  *shadow* ditata di dalam, dan elemen *light* -- di luar, tetapi ada pengecualian penting.\n\nKita akan melihat detailnya di bab berikutnya.\n"
  },
  {
    "path": "8-web-components/5-slots-composition/menu.view/index.html",
    "content": "<!doctype html>\n<template id=\"tmpl\">\n  <style>\n  ul {\n    margin: 0;\n    list-style: none;\n    padding-left: 20px;\n  }\n\n  ::slotted([slot=\"title\"]) {\n    font-size: 18px;\n    font-weight: bold;\n    cursor: pointer;\n  }\n\n  ::slotted([slot=\"title\"])::before {\n    content: '📂';\n    font-size: 14px;\n  }\n\n  .closed ::slotted([slot=\"title\"])::before {\n    content: '📁';\n  }\n\n  .closed ul {\n    display: none;\n  }\n  </style>\n\n  <div class=\"menu\">\n    <slot name=\"title\"></slot>\n    <ul><slot name=\"item\"></slot></ul>\n  </div>\n</template>\n\n<script>\ncustomElements.define('custom-menu', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.append( tmpl.content.cloneNode(true) );\n\n    this.shadowRoot.querySelector('slot[name=\"title\"]').onclick = () => {\n      this.shadowRoot.querySelector('.menu').classList.toggle('closed');\n    };\n  }\n});\n</script>\n\n<custom-menu>\n  <span slot=\"title\">Candy menu</span>\n  <li slot=\"item\">Lollipop</li>\n  <li slot=\"item\">Fruit Toast</li>\n  <li slot=\"item\">Cup Cake</li>\n</custom-menu>\n"
  },
  {
    "path": "8-web-components/6-shadow-dom-style/article.md",
    "content": "# Menata gaya shadow DOM\n\nShadow DOM dapat memuat tag `<style>` dan `<link rel=\"stylesheet\" href=\"…\">`. Dalam kasus terakhir, stylesheet di-cache HTTP, sehingga tidak diunduh ulang untuk beberapa komponen yang menggunakan template yang sama.\n\nSebagai sebuah aturan umum, gaya lokal hanya berfungsi di dalam *shadow tree*, dan gaya dokumen bekerja di luarnya. Tapi ada beberapa pengecualian.\n\n## :host\n\n*Selector* `:host` memungkinkan untuk memilih *shadow host* (elemen yang berisi *shadow tree*).\n\nSebagai contoh, kita membuat elemen `<custom-dialog>` yang harus berada di bagian tengah. Untuk itu kita perlu menata elemen `<custom-dialog>` itu sendiri.\n\nItulah tepatnya yang dilakukan `:host`:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<template id=\"tmpl\">\n  <style>\n    /* the style will be applied from inside to the custom-dialog element */\n    :host {\n      position: fixed;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, -50%);\n      display: inline-block;\n      border: 1px solid red;\n      padding: 10px;\n    }\n  </style>\n  <slot></slot>\n</template>\n\n<script>\ncustomElements.define('custom-dialog', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));\n  }\n});\n</script>\n\n<custom-dialog>\n  Hello!\n</custom-dialog>\n```\n\n## Cascading\n\nShadow host (`<custom-dialog>` itu sendiri) berada di light DOM, sehingga dipengaruhi oleh aturan CSS dokumen.\n\nJika ada properti yang diberikan gaya baik di `:host` secara lokal, dan di dokumen, maka gaya dokumen akan diutamakan.\n\nSebagai contoh, jika dalam dokumen kita memiliki:\n```html\n<style>\ncustom-dialog {\n  padding: 0;\n}\n</style>\n```\n...Maka `<custom-dialog>` tidak akan memiliki padding.\n\nIni sangat memudahkan, karena kita dapat mengatur gaya komponen \"default\" dalam aturan `:host`-nya, dan kemudian dengan mudah menimpanya dalam dokumen.\n\nPengecualiannya adalah ketika properti lokal diberi label `!important`, untuk properti seperti itu, gaya lokal diutamakan.\n\n## :host(selector)\n\nSama seperti `:host`, tetapi hanya diterapkan jika shadow host cocok dengan `selector`.\n\nSebagai contoh, kita ingin membuat `<custom-dialog>` berada di tengah jika hanya memiliki atribut `centered`:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<template id=\"tmpl\">\n  <style>\n*!*\n    :host([centered]) {\n*/!*\n      position: fixed;\n      left: 50%;\n      top: 50%;\n      transform: translate(-50%, -50%);\n      border-color: blue;\n    }\n\n    :host {\n      display: inline-block;\n      border: 1px solid red;\n      padding: 10px;\n    }\n  </style>\n  <slot></slot>\n</template>\n\n<script>\ncustomElements.define('custom-dialog', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'}).append(tmpl.content.cloneNode(true));\n  }\n});\n</script>\n\n\n<custom-dialog centered>\n  Centered!\n</custom-dialog>\n\n<custom-dialog>\n  Not centered.\n</custom-dialog>\n```\n\nSekarang gaya penengah tambahan hanya diterapkan pada dialog pertama: `<custom-dialog centered>`.\n\nRingkasnya, kita dapat menggunakan *selector* `:host` untuk menata elemen utama komponen. Gaya ini (kecuali `!important`) dapat ditimpa oleh dokumen.\n\n## Menata gaya konten yang ber-slot\n\nSekarang mari kita pertimbangkan situasi ketika menggunakan slot.\n\nElemen yang ber-slot berasal dari light DOM, jadi mereka menggunakan gaya dokumen. Gaya lokal tidak memengaruhi konten yang menggunakan slot.\n\nPada contoh dibawah, `<span>` yang berada di slot dicetak tebal, sesuai dengan gaya dokumen, tetapi tidak mengambil `background` dari gaya lokal:\n```html run autorun=\"no-epub\" untrusted height=80\n<style>\n*!*\n  span { font-weight: bold }\n*/!*\n</style>\n\n<user-card>\n  <div slot=\"username\">*!*<span>John Smith</span>*/!*</div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      span { background: red; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nHasilnya dicetak tebal, tapi tidak memiliki latar belakang merah.\n\nJika kita ingin menata gaya pada elemen ber-slot pada komponen kita, ada dua pilihan.\n\nPertama, kita dapat menata gaya `<slot>` itu sendiri dan mengandalkan pewarisan CSS:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<user-card>\n  <div slot=\"username\">*!*<span>John Smith</span>*/!*</div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      slot[name=\"username\"] { font-weight: bold; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nDi sini `<p>John Smith</p>` menjadi tebal, karena pewarisan CSS berlaku di antara `<slot>` dan isinya. Tetapi dalam CSS itu sendiri tidak semua properti diwariskan.\n\nPilihan lainnya adalah menggunakan pseudo-class `::slotted(selector)`. Pseudo-class ini cocok dengan elemen berdasarkan dua kondisi:\n\n1. Elemen itu adalah elemen yang ber-slot, yang berasal dari light DOM. Nama slot tidak masalah. apapun elemen yang ber-slot, tetapi hanya elemen itu sendiri, bukan anak-anaknya.\n2. Elemen cocok dengan `selector`.\n\nDalam contoh kita, `::slotted(div)` memilih dengan tepat `<div slot=\"username\">`, tetapi bukan turunannya:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<user-card>\n  <div slot=\"username\">\n    <div>John Smith</div>\n  </div>\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `\n      <style>\n*!*\n      ::slotted(div) { border: 1px solid red; }\n*/!*\n      </style>\n      Name: <slot name=\"username\"></slot>\n    `;\n  }\n});\n</script>\n```\n\nHarap perhatikan, *selector* `::slotted` tidak dapat turun lebih jauh ke dalam slot. *selector* ini tidak valid:\n\n```css\n::slotted(div span) {\n  /* <div> kita yang ber-slot tidak cocok dengan ini */\n}\n\n::slotted(div) p {\n  /* tidak bisa masuk ke dalam light DOM */\n}\n```\n\nJuga, `::slotted` hanya dapat digunakan di CSS. Kita tidak dapat menggunakannya di `querySelector`.\n\n## CSS hooks dengan properti kustom\nBagaimana kita menata gaya elemen internal komponen dari dokumen utama?\n\n*Selector* seperti `:host` menerapkan aturan ke elemen `<custom-dialog>` atau `<user-card>`, tetapi bagaimana cara menata elemen shadow DOM di dalamnya?\n\nTidak ada *selector* yang dapat secara langsung memengaruhi gaya shadow DOM dari dokumen. Tapi seperti kita mengekspos *method* untuk berinteraksi dengan komponen kita, kita bisa mengekspos variabel CSS (Properti kustom CSS) untuk menatanya.\n\n**Properti kustom CSS ada di semua tingkatan, baik dalam light DOM maupun shadow DOM**\n\nMisalnya, dalam shadow DOM kita dapat menggunakan variabel CSS `--user-card-field-color` untuk menata *field*, dan dokumen luar dapat mengatur nilainya:\n\n```html\n<style>\n  .field {\n    color: var(--user-card-field-color, black);\n    /* jika --user-card-field-color tidak didefinisikan, gunakan warna hitam */\n  }\n</style>\n<div class=\"field\">Name: <slot name=\"username\"></slot></div>\n<div class=\"field\">Birthday: <slot name=\"birthday\"></slot></div>\n```\n\nKemudian, kita dapat mendeklarasikan properti ini di dokumen luar untuk `<user-card>`:\n\n```css\nuser-card {\n  --user-card-field-color: green;\n}\n```\n\nProperti kustom CSS menembus shadow DOM, artinya mereka bisa dilihat di mana-mana, sehingga aturan `.field` yang berada di dalam akan memanfaatkannya.\n\nBerikut contoh lengkapnya:\n\n```html run autorun=\"no-epub\" untrusted height=80\n<style>\n*!*\n  user-card {\n    --user-card-field-color: green;\n  }\n*/!*\n</style>\n\n<template id=\"tmpl\">\n  <style>\n*!*\n    .field {\n      color: var(--user-card-field-color, black);\n    }\n*/!*\n  </style>\n  <div class=\"field\">Name: <slot name=\"username\"></slot></div>\n  <div class=\"field\">Birthday: <slot name=\"birthday\"></slot></div>\n</template>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.append(document.getElementById('tmpl').content.cloneNode(true));\n  }\n});\n</script>\n\n<user-card>\n  <span slot=\"username\">John Smith</span>\n  <span slot=\"birthday\">01.01.2001</span>\n</user-card>\n```\n\n## Ringkasan\n\nShadow DOM dapat menyertakan gaya, seperti `<style>` atau `<link rel=\"stylesheet\">`.\n\nGaya lokal dapat memengaruhi: \n- shadow tree, \n- shadow host dengan pseudoclass `:host` dan `:host()`, \n- elemen yang ber-slot (berasal dari light DOM), `::slotted(selector)` memungkinkan untuk memilih elemen yang ber-slot, tetapi bukan anaknya.\n\nGaya dokumen dapat memengaruhi:\n- shadow host (karena berada di dokumen luar)\n- elemen ber-slot dan isinya (yang juga karena berada di dokumen luar)\n\nSaat properti CSS bertentangan, biasanya gaya dokumen didahulukan, kecuali jika properti diberi label sebagai `!important`. Maka gaya lokal diutamakan.\n\nProperti kustom CSS menembus shadow DOM. Mereka digunakan sebagai \"hooks\" untuk menata gaya komponen:\n\n1. Komponen menggunakan properti CSS kustom untuk menata gaya *key* elemen, seperti `var(--component-name-title, <default value>)`.\n2. Penulis komponen mempublikasikan properti ini untuk para pengembang, mereka sama pentingnya dengan *method* komponen publik lainnya.\n3. Saat pengembang ingin memberi gaya pada *title*, mereka menetapkan properti CSS `--component-name-title` untuk shadow host atau di atasnya.\n4. Profit!\n"
  },
  {
    "path": "8-web-components/7-shadow-dom-events/article.md",
    "content": "# Shadow DOM dan events\n\nIde di balik pohon bayangan adalah untuk mengenkapsulasi detail implementasi internal suatu komponen.\n\nMisalkan, *event* klik terjadi di dalam shadow DOM dari komponen `<user-card>`. Namun skrip di dokumen utama tidak mengetahui internal shadow DOM, terutama jika komponen tersebut berasal dari pustaka pihak ketiga.\n\nJadi, untuk menjaga detailnya tetap terenkapsulasi, browser menargetkan ulang *event* tersebut.\n\n**Event yang terjadi di shadow DOM memiliki elemen host sebagai target, saat tertangkap di luar komponen.**\n\nBerikut contoh sederhananya:\n\n```html run autorun=\"no-epub\" untrusted height=60\n<user-card></user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<p>\n      <button>Click me</button>\n    </p>`;\n    this.shadowRoot.firstElementChild.onclick =\n      e => alert(\"Inner target: \" + e.target.tagName);\n  }\n});\n\ndocument.onclick =\n  e => alert(\"Outer target: \" + e.target.tagName);\n</script>\n```\n\nJika anda mengklik tombol tersebut, pesan yang ditampilkan adalah:\n\n1. Inner target: `BUTTON` -- *event handler* internal mendapatkan target yang benar, yaitu elemen di dalam shadow DOM.\n2. Outer target: `USER-CARD` -- dokumen *event handler* mendapatkan shadow host sebagai target.\n\nPenargetan ulang *event* adalah hal yang bagus untuk dimiliki, karena dokumen luar tidak harus tahu tentang komponen internal. Dari sudut pandang ini, *event* tersebut terjadi di `<user-card>`.\n\n**Penargetan ulang tidak terjadi jika peristiwa terjadi pada *slotted* elemen, yang secara fisik berada di light DOM.**\n\nMisalnya, jika pengguna mengklik `<span slot =\" username \">` pada contoh di bawah, target *event* persis elemen `span` ini, untuk *shadow* dan *light handler*:\n\n```html run autorun=\"no-epub\" untrusted height=60\n<user-card id=\"userCard\">\n*!*\n  <span slot=\"username\">John Smith</span>\n*/!*\n</user-card>\n\n<script>\ncustomElements.define('user-card', class extends HTMLElement {\n  connectedCallback() {\n    this.attachShadow({mode: 'open'});\n    this.shadowRoot.innerHTML = `<div>\n      <b>Name:</b> <slot name=\"username\"></slot>\n    </div>`;\n\n    this.shadowRoot.firstElementChild.onclick =\n      e => alert(\"Inner target: \" + e.target.tagName);\n  }\n});\n\nuserCard.onclick = e => alert(`Outer target: ${e.target.tagName}`);\n</script>\n```\n\nJika klik terjadi pada `\"John Smith\"`, untuk *inner* dan *outer handler*, targetnya adalah `<span slot =\" username \">`. Itu adalah elemen dari light DOM, jadi tidak ada penargetan ulang.\n\nDi sisi lain, jika klik terjadi pada elemen yang berasal dari shadow DOM, misalnya pada `<b> Name </b>`, lalu, saat ia menggelembung keluar dari shadow DOM, `event.target`-nya disetel ulang ke `<user-card>`.\n\n## Bubbling, event.composedPath()\n\nUntuk tujuan *event bubbling*, DOM yang diratakan digunakan.\n\nJadi, jika kita memiliki *slotted* elemen, dan sebuah *event* terjadi di suatu tempat di dalamnya, maka *event* itu menggelembung ke `<slot>` dan ke atasnya.\n\nJalur lengkap ke target *event* asli, dengan semua elemen bayangan, bisa diperoleh menggunakan `event.composedPath()`. Seperti yang dapat kita lihat dari nama *method*, jalur tersebut diambil setelah komposisi.\n\nDalam contoh di atas, DOM yang diratakan adalah:\n\n```html\n<user-card id=\"userCard\">\n  #shadow-root\n    <div>\n      <b>Name:</b>\n      <slot name=\"username\">\n        <span slot=\"username\">John Smith</span>\n      </slot>\n    </div>\n</user-card>\n```\n\n\nJadi, untuk klik pada `<span slot=\"username\">`, panggilan ke `event.composedPath()` mengembalikan sebuah array: [`span`,  `slot`, `div`, `shadow-root`, `User-card`, `body`, `html`,  `document`, `window`]. Itu persis seperti rantai induk dari elemen target di DOM yang diratakan, setelah komposisi.\n\n```warn header=\"Shadow tree details are only provided for `{mode:'open'}` trees\"\nJika pohon bayangan dibuat dengan `{mode: 'closed'}`, maka jalur yang dibuat dimulai dari host: `user-card` dan ke atas.\n\nItu prinsip yang sama untuk *methods* lain yang bekerja dengan shadow DOM. Bagian dalam pohon tertutup benar-benar tersembunyi.\n```\n\n\n## event.composed\n\nSebagian besar event berhasil menggelembung melewati batas shadow DOM. Ada beberapa event yang tidak.\n\nIni diatur oleh properti event objek `kompos`. Jika itu `true`, maka event tersebut melewati batas. Jika tidak, event itu hanya bisa ditangkap dari dalam shadow DOM.\n\nJika anda melihat [UI Events specification](https://www.w3.org/TR/uievents), sebagian besar event memiliki `composed: true`:\n\n- `blur`, `focus`, `focusin`, `focusout`,\n- `click`, `dblclick`,\n- `mousedown`, `mouseup` `mousemove`, `mouseout`, `mouseover`,\n- `wheel`,\n- `beforeinput`, `input`, `keydown`, `keyup`.\n\nSemua event sentuh dan event penunjuk juga memiliki `composed: true`.\n\nAda beberapa event yang memiliki `compose: false`:\n\n- `mouseenter`, `mouseleave` (they do not bubble at all),\n- `load`, `unload`, `abort`, `error`,\n- `select`,\n- `slotchange`.\n\nEvent ini hanya dapat ditangkap pada elemen dalam DOM yang sama, tempat target event berada.\n\n## Event kustom\n\nSaat kita mengirimkan event khusus, kita perlu mengatur properti `bubble` dan `composed` ke `true` agar dapat menggelembung dan keluar dari komponen.\n\nMisalnya, di sini kita membuat `div#inner` di shadow DOM dari `div#outer` dan memicu dua event di atasnya. Hanya satu dengan `composed: true` yang membuatnya berada di luar dokumen:\n\n```html run untrusted height=0\n<div id=\"outer\"></div>\n\n<script>\nouter.attachShadow({mode: 'open'});\n\nlet inner = document.createElement('div');\nouter.shadowRoot.append(inner);\n\n/*\ndiv(id=outer)\n  #shadow-dom\n    div(id=inner)\n*/\n\ndocument.addEventListener('test', event => alert(event.detail));\n\ninner.dispatchEvent(new CustomEvent('test', {\n  bubbles: true,\n*!*\n  composed: true,\n*/!*\n  detail: \"composed\"\n}));\n\ninner.dispatchEvent(new CustomEvent('test', {\n  bubbles: true,\n*!*\n  composed: false,\n*/!*\n  detail: \"not composed\"\n}));\n</script>\n```\n\n## Ringkasan\n\n*Event* hanya melewati batas shadow DOM jika flag `composed` disetel ke `true`.\n\n*Event* bawaan sebagian besar memiliki `composed: true`, seperti yang dijelaskan dalam spesifikasi yang relevan:\n\n- UI Events <https://www.w3.org/TR/uievents>.\n- Touch Events <https://w3c.github.io/touch-events>.\n- Pointer Events <https://www.w3.org/TR/pointerevents>.\n- ...Dan seterusnya.\n\nBeberapa *event* bawaan yang memiliki `composed: false`:\n\n- `mouseenter`, `mouseleave` (juga tidak menggelembung),\n- `load`, `unload`, `abort`, `error`,\n- `select`,\n- `slotchange`.\n\n*Events* ini hanya dapat ditangkap pada elemen dalam DOM yang sama.\n\nJika kita mengirimkan `CustomEvent`, maka kita harus secara eksplisit mengatur `composed: true`.\n\nHarap diperhatikan bahwa dalam kasus komponen bertingkat, satu shadow DOM dapat bertumpuk ke dalam yang lain. Dalam kasus tersebut, gelembung peristiwa yang disusun melalui semua batas shadow DOM. Jadi, jika sebuah peristiwa hanya ditujukan untuk komponen penutup langsung, kita juga dapat mengirimkannya ke host bayangan dan menyetel `composed: false`. Kemudian keluar dari komponen shadow DOM, tetapi tidak akan mengarah ke DOM tingkat yang lebih tinggi.\n"
  },
  {
    "path": "8-web-components/index.md",
    "content": "# Web components\n\nWeb components is a set of standards to make self-contained components: custom HTML-elements with their own properties and methods, encapsulated DOM and styles.\n"
  },
  {
    "path": "9-regular-expressions/01-regexp-introduction/article.md",
    "content": "# Pola dan flag\n\nEkspresi reguler merupakan cara ampuh untuk mencari dan mengganti string.\n\nDi JavaScript, mereka tersedia melalui objek [RegExp](mdn:js/RegExp) dan juga terintegrasi dalam metode string.\n\n## Ekspresi Reguler\n\nEkspresi reguler (\"regexp\", atau hanya \"reg\") terdiri dari *pola* dan *flag* opsional.\n\nAda dua syntax yang bisa dipakai untuk membuat objek ekspresi reguler.\n\nSyntax panjang:\n\n```js\nregexp = new RegExp(\"pattern\", \"flag\");\n```\n\nDan yang \"pendek\", menggunakan garis miring `\"/\"`:\n\n```js\nregexp = /pattern/; // tanpa flag\nregexp = /pattern/gmi; // dengan flag g,m dan i (untuk segera ditutup)\n```\n\nGaris miring `pattern:/.../` memberitahu JavaScript bahwa kita sedang membuat ekspresi regular. Mereka memiliki peran yang sama dengan tanda petik untuk *string*.\n\nUntuk kedua kasus di atas `regexp` menjadi instance dari built-in object kelas `RegExp`.\n\nPerbedaan utama antara kedua syntax ini adalah garis miring `pattern:/.../` melarang penyisipan ekspresi (seperti string dengan `${...}`). Mereka benar-benar static.\n\nGaris miring digunakan saat kita tahu ekspresi regular saat menulis kode -- dan itu situasi paling umum. Sedangkan `RegExp baru`, lebih sering dipakai saat kita harus membuat regexp baru \"on the fly\" dari string yang dihasilkan secara dinamis. Misalnya:\n\n```js\nlet tag = prompt(\"What tag do you want to find?\", \"h2\");\n\nlet regexp = new RegExp(`<${tag}>`); // sama dengan /<h2>/ jika dijawab \"h2\" di prompt di atas\n```\n\n## Flag\n\nExpresi regular bisa punya flag yang mempengaruhi pencarian.\n\nCuma ada 6 di antaranya di JavaScript:\n\n`pattern:i`\n: Dengan flag ini pencarian case-insensitive: tak ada perbedaan antara `A` dan `a` (lihat contoh di bawah).\n\n`pattern:g`\n: Dengan flag ini pencarian mencari semua kecocokan, tanpanya -- hanya yang pertama yang dikembalikan.\n\n`pattern:m`\n: Mode baris-ganda (dibahas di bab <info:regexp-multiline-mode>).\n\n`pattern:s`\n: Menyalakan mode \"dotall\", yang membolehkan dot `pattern:.` untuk cocok dengan karakter baris-baru `\\n` (dibahas di bab <info:regexp-character-classes>).\n\n`pattern:u`\n: Menyalakan dukungan penuh unicode. Flag ini menyalakan pemrosesan yang benar dari pasangan pengganti. Lebih lanjut tentang itu di bab <info:regexp-unicode>.\n\n`pattern:y`\n: Mode \"sticky\": mencari posisi tepat di dalam teks  (dibahas di bab <info:regexp-sticky>)\n\n```smart header=\"Warna\"\nSkema warnanya adalah:\n\n- regexp -- `pattern:merah`\n- string (tempat kita mencari) -- `subject:biru`\n- result -- `match:hijau`\n```\n\n## Pencarian: str.match\n\nSeperti yang dikatakan sebelumnya, expresi regular terintegrasi dengan metode string.\n\nMetode ini `str.match(regexp)` mencari semua kecocokan `regexp` di dalam string `str`.\n\nIa punya 3 mode kerja:\n\n1. Jika expresi regular punya flag `pattern:g`, ia mengembalikan array semua kecocokan:\n    ```js run\n    let str = \"We will, we will rock you\";\n\n    alert( str.match(/we/gi) ); // We,we (array 2 substring yang cocok)\n    ```\n    Tolong ingat bahwa kedua `match:We` dan `match:we` ditemukan, karena flag `pattern:i` membuat regular expression case-insensitive.\n\n2. Jika tak ada flag macam ini ia mengembalikan hanya kecocokan pertama dalam bentuk array, dengan kecocokan penuh pada index `0` dan beberapa detil tambahan di properti:\n    ```js run\n    let str = \"We will, we will rock you\";\n\n    let result = str.match(/we/i); // tanpa flag g\n\n    alert( result[0] );     // We (1st match)\n    alert( result.length ); // 1\n\n    // Detil:\n    alert( result.index );  // 0 (posisi kecocokan)\n    alert( result.input );  // We will, we will rock you (source string)\n    ```\n    Array itu bisa punya index lain, selain `0` jika satu bagian expresi regular disertakan dalam tanda kurung. Kita akan bahas itu di bab <info:regexp-groups>.\n\n3. Dan, akhirnya, jika tak ada kecocokan, `null` dikembalikan (tak peduli apakah ada flag `pattern:g` atau tidak).\n\n    Itu nuansa paling penting. Jika tak ada kecocokan, kita tak mendapatkan array kosong, melainkan `null`. Melupakan itu bisa membawa galat, misal:\n\n    ```js run\n    let matches = \"JavaScript\".match(/HTML/); // = null\n\n    if (!matches.length) { // Error: Cannot read property 'length' of null\n      alert(\"Error in the line above\");\n    }\n    ```\n\n    Jika kita mau hasilnya selalu array, kita bisa menulisnya seperti ini:\n\n    ```js run\n    let matches = \"JavaScript\".match(/HTML/)*!* || []*/!*;\n\n    if (!matches.length) {\n      alert(\"No matches\"); // sekarang ini bekerja\n    }\n    ```\n\n## Penggantian: str.replace\n\nMetode `str.replace(regexp, replacement)` mengganti kecocokan dengan `regexp` dalam string `str` dengan `replacement` (semua cocok jika ada flag `pattern:g`, kalau tidak, cuma yang pertama saja).\n\nMisalnya:\n\n```js run\n// tak ada flag g\nalert( \"We will, we will\".replace(/we/i, \"I\") ); // I will, we will\n\n// dengan flag g\nalert( \"We will, we will\".replace(/we/ig, \"I\") ); // I will, I will\n```\n\nArgumen kedua adalah string `replacement`. Kita bisa memakai kombinasi karakter spesial di dalamnya untuk menyisipkan fragment kecocokan:\n\n| Simbol | Aksi di string pengganti |\n|--------|--------|\n|`$&`|menyisipkan seluruh kecocokan|\n|<code>$&#096;</code>|menyisipkan bagian string sebelum kecocokan|\n|`$'`|menyisipkan bagian string setelah kecocokan|\n|`$n`|jika `n` angka 1-2 digit, maka ia menyisipkan konten tanda kurung ke-n, lebih lanjut tentang itu di bab <info:regexp-groups>|\n|`$<name>`|menyisipkan konten tanda kurung dengan `name` yang diberikan, lebih lanjut tentang ini ada di bab <info:regexp-groups>|\n|`$$`|menyisipkan karakter `$` |\n\nContohnya dengan `pattern:$&`:\n\n```js run\nalert( \"I love HTML\".replace(/HTML/, \"$& and JavaScript\") ); // I love HTML and JavaScript\n```\n\n## Pengujian: regexp.test\n\nMetode `regexp.test(str)` mencari paling tidak 1 kecocokan, jika ditemukan, mengembalikan `true`, kalau tidak `false`.\n\n```js run\nlet str = \"I love JavaScript\";\nlet regexp = /LOVE/i;\n\nalert( regexp.test(str) ); // true\n```\n\nLebih lanjut di bab ini kita akan mempelajari lebih expresi regular, menjumpai banyak contoh, dan menemui metode lainnya.\n\nInformasi penuh tentang metode ini diberikan dalam artikel <info:regexp-methods>.\n\n## Ringkasan\n\n- Expresi regular terdiri atas pola dan flag opsional: `pattern:g`, `pattern:i`, `pattern:m`, `pattern:u`, `pattern:s`, `pattern:y`.\n- Tanpa flag dan simbol spesial yang akan kita pelajari nanti, pencarian menggunakan regexp sama dengan pencarian substring.\n- Metode `str.match(regexp)` mencari kecocokan: semuanya jika ada flag `pattern:g`, kalau tidak, cuma yang pertama saja.\n- Metode `str.replace(regexp, replacement)` mengganti kecocokan dengan `regexp` by `replacement`: semuanya jika ada flag `pattern:g`, selain itu cuma yang pertama.\n- Metode `regexp.test(str)` mengembalikan `true` jika paling tidak ada satu yang cocok, kalau tidak ada, ia mengembalikan `false`.\n"
  },
  {
    "path": "9-regular-expressions/02-regexp-character-classes/article.md",
    "content": "# Kelas-kelas Karakter (Character classes)\n\nBayangkan sebuah tugas praktik -- kita memiliki sebuah nomor telepon `\"+7(903)-123-45-67\"`, dan kita harus mengubah nomor telepon tersebut menjadi angka murni: `79031234567`.\n\nUntuk melakukan hal itu, kita harus menemukan apapun yang bukan merupakan angka. Kelas karakter dapat membantu membereskan hal tersebut.\n\nSebuah *Kelas Karakter* adalah sebuah notasi yang spesial yang membandingkan simbol apapun dengan aturan tertentu.\n\nUntuk memulai, ayo kita *explore* kelas \"angka\" (digit). Kelas \"angka\" ditulis seperti `pattern:\\d` dan dapat disamakan dengan \"setiap angka yang ada\".\n\nUntuk contoh, kita akan mencoba mencari angka pertama pada nomor telepon:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet regexp = /\\d/;\n\nalert( str.match(regexp) ); // 7\n```\n\nTanpa tanda `pattern:g`, ekspresi regularnya hanya akan mencari satu kecocokan, yaitu angka pertama `pattern:\\d`.\n\nCoba kita tambahkan tanda `pattern:g` untuk mencari seluruh angka:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet regexp = /\\d/g;\n\nalert( str.match(regexp) ); // array: 7,9,0,3,1,2,3,4,5,6,7\n\n// kita coba untuk membuat \"hanya angka\" dari nomor teleponnya:\nalert( str.match(regexp).join('') ); // 79031234567\n```\n\nItu adalah kelas karakter untuk angka. Tentu saja ada karakter kelas lainnya.\n\nYang paling banyak digunakan adalah:\n\n`pattern:\\d` (\"d\" untuk \"digit\" / \"angka\")\n: Angka: adalah karakter dari `0` sampai `9`.\n\n`pattern:\\s` (\"s\" untuk \"space\" / \"spasi\" / untuk mencari *whitespace*)\n: Simbol *space* / spasi: termasuk spasi, *tabs* `\\t`, garis baru `\\n` dan beberapa karakter lainnya yang jarang digunakan, seperti `\\v`, `\\f` dan `\\r`.\n\n`pattern:\\w` (\"w\" untuk \"word\" / \"karakter kata\")\n: \"karakter kata\": antara sebuah huruf dari alfabet latin atau sebuah angka atau sebuah *underscore* `_` (a-z, A-Z, 0-9). `pattern:\\w` tidak bisa digunakan untuk huruf yang bukan huruf latin (seperti *cyrillic* atau *hindi*).\n\nContohnya, `pattern:\\d\\s\\w` menandakan sebuah \"digit\" yang diikuti dengan \"karakter *space*\" diikuti dengan \"karakter kata\", seperti `match:1 a`.\n\n**Regular ekspresi mungkin mengandung simbol dan kelas karakter.**\n\nContoh, `pattern:CSS\\d` cocok dengan *string* `match:CSS` dengan angka setelahnya:\n\n```js run\nlet str = \"Apakah CSS4 telah rilis?\";\nlet regexp = /CSS\\d/\n\nalert( str.match(regexp) ); // CSS4\n```\n\nDan juga kita bisa menggunakan banyak kelas karakter:\n\n```js run\nalert( \"I love HTML5!\".match(/\\s\\w\\w\\w\\w\\d/) ); // ' HTML5'\n```\n\nKecocokannya (setiap karakter kelas regexp yang menghasilkan hasil yang sesuai):\n\n![](love-html5-classes.svg)\n\n## Kelas kebalikan (Inverse Classes)\n\nUntuk setiap kelas karakter yang ada terdapat sebuah \"kelas kebalikan\", ditunjukan dengan huruf yang sama, tapi dengan huruf besar / kapital.\n\n\"Kebalikan\" berarti kelas tersebut cocok dengan seluruh karakter lainnya, contoh:\n\n`pattern:\\D`\n: Bukan angka: karakter apapun kecuali `pattern:\\d`, contoh huruf.\n\n`pattern:\\S`\n: Bukan spasi: karakter apapun kecuali `pattern:\\s`, contoh huruf.\n\n`pattern:\\W`\n: Bukan karakter kata: apapun kecuali `pattern:\\w`, contoh huruf yang bukan latin atau sebuah spasi.\n\nPada awal bab kita melihat bagaimana cara membuat nomor telepon yang hanya angka dari string seperti `subject:+7(903)-123-45-67`: cari seluruh angka dan gabungkan.\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nalert( str.match(/\\d/g).join('') ); // 79031234567\n```\n\nAlternatif, cara pendeknya adalah dengan mencari karakter yang bukan angka dan hilangkan dari stringnya:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nalert( str.replace(/\\D/g, \"\") ); // 79031234567\n```\n\n## Sebuah titik adalah \"karakter apapun\"\n\nSebuah titik `pattern:.` adalah sebuah kelas karakter spesial yang cocok \"dengan karakter apapun kecuali garis baru\".\n\nContoh:\n\n```js run\nalert( \"Z\".match(/./) ); // Z\n```\n\nAtau ditengah dari sebuah ekspresi regular:\n\n```js run\nlet regexp = /CS.4/;\n\nalert( \"CSS4\".match(regexp) ); // CSS4\nalert( \"CS-4\".match(regexp) ); // CS-4\nalert( \"CS 4\".match(regexp) ); // CS 4 (spasi juga merupakan karakter)\n```\n\nIngatlah bahwa titik berarti \"karakter apapun\", tapi bukan sebuah \"ketiadaan karakter\". Harusnya ada karakter yang mirip dengannya:\n\n```js run\nalert( \"CS4\".match(/CS.4/) ); // null, tidak ada yang cocok karena tidak terdapat karakter untuk titiknya.\n```\n\n### Titik secara literal adalah karakter apapun dengan tanda \"s\"\n\nPada dasarnya, sebuah titik tidak cocok dengan karakter garis baru `\\n`.\n\nContoh, ekspresi regular `pattern:A.B` cocok dengan `match:A`, dan lalu `match:B` dengan karakter apapun diantaranya, kecuali garis baru `\\n`:\n\n```js run\nalert( \"A\\nB\".match(/A.B/) ); // null (tidak ada yang cocok)\n```\n\nTerdapat beberapa situasi ketika kita ingin memperlakukan titik sebagai \"karakter apapun\", garis baru termasuk.\n\nItulah yang dilakukan oleh `pattern:s`. Jika sebuah ekspresi regular memilikinya, maka sebuah titik `pattern:.` secara literal cocok dengan karakter apapun:\n\n```js run\nalert( \"A\\nB\".match(/A.B/s) ); // A\\nB (cocok!)\n```\n\n````warn header=\"Tidak didukung IE\"\nTanda `pattern:s` tidak didukung pada IE.\n\nBeruntungnya, masih terdapat alternatif untuk hal itu yang mana dapat berjalan dimanapun. Kita bisa menggunakan *regexp* seperti `pattern:[\\s\\S]` untuk dicocokan dengan \"karakter apapun\" (pola ini akan dijelaskan pada bab <info:regexp-character-sets-and-ranges>).\n\n```js run\nalert( \"A\\nB\".match(/A[\\s\\S]B/) ); // A\\nB (cocok!)\n```\n\nPola `pattern:[\\s\\S]` bisa dikatakan: \"sebuah karakter spasi ATAU bukan sebuah karakter spasi\". Dengan kata lain, \"apapun\". Kita bisa menggunakan kelas komplemen pasangan lainnya, seperti `pattern:[\\d\\D]`, itu tidak penting. Atau bahkan `pattern:[^]` -- sama seperti mencocokan karakter apapun kecuali tidak ada.\n\nKita juga bisa menggunakan trik ini jika kita ingin dua bentuk \"titik\" didalam pola yang sama: titik yang sebenarnya `pattern:.` bertingkah seperti biasa (\"tidak termasuk garis baru\"), dan juga sebuah cara untuk mencocokan \"karakter apapun\" dengan `pattern:[\\s\\S]` atau sejenisnya.\n````\n\n````warn header=\"Perhatikan spasinya\"\nBiasanya kita hanya memberikan sedikit perhatian pada spasi. Untuk kita string `subject:1-5` dan `subject:1 - 5` adalah hal yang hampir mirip.\n\nTapi jika sebuah ekspresi regular tidak memperhatikan spasinya, maka pencocokannya akan gagal.\n\nKita coba untuk mencari angka yang dipisahkan dengan tanda penghubung:\n\n```js run\nalert( \"1 - 5\".match(/\\d-\\d/) ); // null, tidak cocok!\n```\n\nSekarang kita benarkan dengan menambahkan spasi pada regexp-nya `pattern: \\d - \\d`:\n\n```js run\nalert( \"1 - 5\".match(/\\d - \\d/) ); // 1 - 5, sekarang pencocokannya bekerja\n// atau kita bisa menggunakan kelas \\s:\nalert( \"1 - 5\".match(/\\d\\s-\\s\\d/) ); // 1 - 5, juga bekerja\n```\n\n**Sebuah spasi adalah sebuah karakter. Sama pentingnya dengan karakter lainnya**\n\nKita tidak bisa menambah atau menghilangkan spasi dari ekspresi regular dan berharap pencocokanya akan berjalan sama.\n\nDengan kata lain, didalam ekspresi regular seluruh karakter bersifat penting, spasi pun sama.\n````\n\n## Ringkasan\n\nTerdapat karakter kelas:\n\n- `pattern:\\d` -- digits / Angka.\n- `pattern:\\D` -- non-digits / Bukan angka.\n- `pattern:\\s` -- simbol spasi, tabs, garis baru.\n- `pattern:\\S` -- semuanya kecuali `pattern:\\s`.\n- `pattern:\\w` -- huruf latin, angka, *underscore* `'_'`.\n- `pattern:\\W` -- semuanya kecuali `pattern:\\w`.\n- `pattern:.` -- karakter apapun jika dengan regexp tanda `'s'`, sebaliknya semuanya kecuali sebuah garis baru `\\n`.\n\n...Tapi ini belum semuanya!\n\n*Unicode encoding*, digunakan oleh Javascript untuk *string*, menyediakan berbagai properti untuk karakter, seperti: bahasa mana yang cocok dengan surat ini (jika sebuah surat), tanda bacanya, dll.\n\nWe can search by these properties as well. That requires flag `pattern:u`, covered in the next article.\nTentu saja kita bisa mencari dengan propertinya. Yang mana membutuhkan tanda `pattern:u`, yang akan dibahas didalam bab selanjutnya.\n"
  },
  {
    "path": "9-regular-expressions/03-regexp-unicode/article.md",
    "content": "# Unicode: flag \"u\" and class \\p{...}\n\nJavaScript uses [Unicode encoding](https://en.wikipedia.org/wiki/Unicode) for strings. Most characters are encoded with 2 bytes, but that allows to represent at most 65536 characters.\n\nThat range is not big enough to encode all possible characters, that's why some rare characters are encoded with 4 bytes, for instance like `𝒳` (mathematical X) or `😄` (a smile), some hieroglyphs and so on.\n\nHere are the Unicode values of some characters:\n\n| Character  | Unicode | Bytes count in Unicode  |\n|------------|---------|--------|\n| a | `0x0061` |  2 |\n| ≈ | `0x2248` |  2 |\n|𝒳| `0x1d4b3` | 4 |\n|𝒴| `0x1d4b4` | 4 |\n|😄| `0x1f604` | 4 |\n\nSo characters like `a` and `≈` occupy 2 bytes, while codes for `𝒳`, `𝒴` and `😄` are longer, they have 4 bytes.\n\nLong time ago, when JavaScript language was created, Unicode encoding was simpler: there were no 4-byte characters. So, some language features still handle them incorrectly.\n\nFor instance, `length` thinks that here are two characters:\n\n```js run\nalert('😄'.length); // 2\nalert('𝒳'.length); // 2\n```\n\n...But we can see that there's only one, right? The point is that `length` treats 4 bytes as two 2-byte characters. That's incorrect, because they must be considered only together (so-called \"surrogate pair\", you can read about them in the article <info:string>).\n\nBy default, regular expressions also treat 4-byte \"long characters\" as a pair of 2-byte ones. And, as it happens with strings, that may lead to odd results. We'll see that a bit later, in the article <info:regexp-character-sets-and-ranges>.\n\nUnlike strings, regular expressions have flag `pattern:u` that fixes such problems. With such flag, a regexp handles 4-byte characters correctly. And also Unicode property search becomes available, we'll get to it next.\n\n## Unicode properties \\p{...}\n\nEvery character in Unicode has a lot of properties. They describe what \"category\" the character belongs to, contain miscellaneous information about it.\n\nFor instance, if a character has `Letter` property, it means that the character belongs to an alphabet (of any language). And `Number` property means that it's a digit: maybe Arabic or Chinese, and so on.\n\nWe can search for characters with a property, written as `pattern:\\p{…}`. To use `pattern:\\p{…}`, a regular expression must have flag `pattern:u`.\n\nFor instance, `\\p{Letter}` denotes a letter in any language. We can also use `\\p{L}`, as `L` is an alias of `Letter`. There are shorter aliases for almost every property.\n\nIn the example below three kinds of letters will be found: English, Georgian and Korean.\n\n```js run\nlet str = \"A ბ ㄱ\";\n\nalert( str.match(/\\p{L}/gu) ); // A,ბ,ㄱ\nalert( str.match(/\\p{L}/g) ); // null (no matches, \\p doesn't work without the flag \"u\")\n```\n\nHere's the main character categories and their subcategories:\n\n- Letter `L`:\n  - lowercase `Ll`\n  - modifier `Lm`,\n  - titlecase `Lt`,\n  - uppercase `Lu`,\n  - other `Lo`.\n- Number `N`:\n  - decimal digit `Nd`,\n  - letter number `Nl`,\n  - other `No`.\n- Punctuation `P`:\n  - connector `Pc`,\n  - dash `Pd`,\n  - initial quote `Pi`,\n  - final quote `Pf`,\n  - open `Ps`,\n  - close `Pe`,\n  - other `Po`.\n- Mark `M` (accents etc):\n  - spacing combining `Mc`,\n  - enclosing `Me`,\n  - non-spacing `Mn`.\n- Symbol `S`:\n  - currency `Sc`,\n  - modifier `Sk`,\n  - math `Sm`,\n  - other `So`.\n- Separator `Z`:\n  - line `Zl`,\n  - paragraph `Zp`,\n  - space `Zs`.\n- Other `C`:\n  - control `Cc`,\n  - format `Cf`,\n  - not assigned `Cn`,\n  - private use `Co`,\n  - surrogate `Cs`.\n\n\nSo, e.g. if we need letters in lower case, we can write `pattern:\\p{Ll}`, punctuation signs: `pattern:\\p{P}` and so on.\n\nThere are also other derived categories, like:\n- `Alphabetic` (`Alpha`), includes Letters `L`, plus letter numbers `Nl` (e.g. Ⅻ - a character for the roman number 12), plus some other symbols `Other_Alphabetic` (`OAlpha`).\n- `Hex_Digit` includes hexadecimal digits: `0-9`, `a-f`.\n- ...And so on.\n\nUnicode supports many different properties, their full list would require a lot of space, so here are the references:\n\n- List all properties by a character: <https://unicode.org/cldr/utility/character.jsp>.\n- List all characters by a property: <https://unicode.org/cldr/utility/list-unicodeset.jsp>.\n- Short aliases for properties: <https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt>.\n- A full base of Unicode characters in text format, with all properties, is here: <https://www.unicode.org/Public/UCD/latest/ucd/>.\n\n### Example: hexadecimal numbers\n\nFor instance, let's look for hexadecimal numbers, written as `xFF`, where `F` is a hex digit (0..9 or A..F).\n\nA hex digit can be denoted as `pattern:\\p{Hex_Digit}`:\n\n```js run\nlet regexp = /x\\p{Hex_Digit}\\p{Hex_Digit}/u;\n\nalert(\"number: xAF\".match(regexp)); // xAF\n```\n\n### Example: Chinese hieroglyphs\n\nLet's look for Chinese hieroglyphs.\n\nThere's a Unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list](https://en.wikipedia.org/wiki/Script_(Unicode)).\n\nTo look for characters in a given writing system we should use `pattern:Script=<value>`, e.g. for Cyrillic letters: `pattern:\\p{sc=Cyrillic}`, for Chinese hieroglyphs: `pattern:\\p{sc=Han}`, and so on:\n\n```js run\nlet regexp = /\\p{sc=Han}/gu; // returns Chinese hieroglyphs\n\nlet str = `Hello Привет 你好 123_456`;\n\nalert( str.match(regexp) ); // 你,好\n```\n\n### Example: currency\n\nCharacters that denote a currency, such as `$`, `€`, `¥`, have Unicode property  `pattern:\\p{Currency_Symbol}`, the short alias: `pattern:\\p{Sc}`.\n\nLet's use it to look for prices in the format \"currency, followed by a digit\":\n\n```js run\nlet regexp = /\\p{Sc}\\d/gu;\n\nlet  str = `Prices: $2, €1, ¥9`;\n\nalert( str.match(regexp) ); // $2,€1,¥9\n```\n\nLater, in the article <info:regexp-quantifiers> we'll see how to look for numbers that contain many digits.\n\n## Summary\n\nFlag `pattern:u` enables the support of Unicode in regular expressions.\n\nThat means two things:\n\n1. Characters of 4 bytes are handled correctly: as a single character, not two 2-byte characters.\n2. Unicode properties can be used in the search: `\\p{…}`.\n\nWith Unicode properties we can look for words in given languages, special characters (quotes, currencies) and so on.\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/1-start-end/solution.md",
    "content": "An empty string is the only match: it starts and immediately finishes.\n\nThe task once again demonstrates that anchors are not characters, but tests.\n\nThe string is empty `\"\"`. The engine first matches the `pattern:^` (input start), yes it's there, and then immediately the end `pattern:$`, it's here too. So there's a match.\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/1-start-end/task.md",
    "content": "# Regexp ^$\n\nWhich string matches the pattern `pattern:^$`?\n"
  },
  {
    "path": "9-regular-expressions/04-regexp-anchors/article.md",
    "content": "# Anchors: mulai _string_ ^ dan akhiran $\n\nTanda karakter _caret_ `^` dan tanda _dollar_ `$` memiliki arti spesial pada _regexp_. Tanda-tanda tersebut dipanggil dengan nama _\"Ancors\"_.\n\nTanda _caret_ `^` membandingkan awalan dari _text_, dan tanda _dollar_ `$` -- membandingkan akhirannya.\n\nContoh, Kita coba jika _text_ nya memiliki awalan `Mary`:\n\n```js run\nlet str1 = \"Mary had a little lamb\";\nalert(/^Mary/.test(str1)); // true\n```\n\nPola `^Mary` berarti: \"_text_ nya dimulai dengan Mary\"\n\nSama seperti ini, kita bisa mencoba jika _text_ nya berakhiran dengan `snow` menggunakan `snow$`:\n\n```js run\nlet str1 = \"it's fleece was white as snow\";\nalert(/snow$/.test(str1)); // true\n```\n\nDalam beberapa kasus tertentu kita bisa menggunakan metode _string_ `startsWith/endsWith`. _Regular Expression_ hanya digunakan untuk _test_ yang lebih kompleks.\n\n## Membandingkan keseluruhannya\n\nKedua _achors_ `^...%` sering digunakan untuk menguji apakah sebuah _string_ sama keseluruhannya secara pola atau tidak. Contohnya, untuk memeriksa apakah _input_ dari _user_ menggunakan format yang benar.\n\nKita coba apakah `12:34` adalah format _string_ atau bukan. Menggunakan: dua angka, lalu titik dua, lalu dua angka lagi.\n\nDidalam bahasa _regular expressions_ itu adalah `pola:\\d\\d:\\d\\d`:\n\n```js run\nlet goodInput = \"12:34\";\nlet badInput = \"12:345\";\n\nlet regexp = /^\\d\\d:\\d\\d$/;\nalert(regexp.test(goodInput)); // true\nalert(regexp.test(badInput)); // false\n```\n\nDisini yang cocok untuk `pola:\\d\\d:\\d\\d` harus dimulai dengan `^` dan diakhiri dengan `$` yang mana harus cocok.\n\nSeluruh _string_ nya harus menggunakan format yang sama. Jika ada yang berbeda atau ada karakter lebih, maka hasilnya akan menjadi `false`.\n\n_Anchors_ bertindak berbeda jika tanda `pola:m` ada. Kita akan mempelajarinya di bab selanjutnya.\n\n```smart header=\"Anchors memiliki \"zero width\"\"\nAnchors `pola:^` dan `pola:$` adalah test. Keduanya memiliki \"zero width\".\n\nDengan kata lain, keduanya tidak mencocokan karakter, akan tetapi memaksa regexp untuk memeriksa kondisi (awal text/akhir text)\n```\n"
  },
  {
    "path": "9-regular-expressions/05-regexp-multiline-mode/article.md",
    "content": "# Multiline mode of anchors ^ $, flag \"m\"\n\nThe multiline mode is enabled by the flag `pattern:m`.\n\nIt only affects the behavior of `pattern:^` and `pattern:$`.\n\nIn the multiline mode they match not only at the beginning and the end of the string, but also at start/end of line.\n\n## Searching at line start ^\n\nIn the example below the text has multiple lines. The pattern `pattern:/^\\d/gm` takes a digit from the beginning of each line:\n\n```js run\nlet str = `1st place: Winnie\n2nd place: Piglet\n3rd place: Eeyore`;\n\n*!*\nalert( str.match(/^\\d/gm) ); // 1, 2, 3\n*/!*\n```\n\nWithout the flag `pattern:m` only the first digit is matched:\n\n```js run\nlet str = `1st place: Winnie\n2nd place: Piglet\n3rd place: Eeyore`;\n\n*!*\nalert( str.match(/^\\d/g) ); // 1\n*/!*\n```\n\nThat's because by default a caret `pattern:^` only matches at the beginning of the text, and in the multiline mode -- at the start of any line.\n\n```smart\n\"Start of a line\" formally means \"immediately after a line break\": the test  `pattern:^` in multiline mode matches at all positions preceded by a newline character `\\n`.\n\nAnd at the text start.\n```\n\n## Searching at line end $\n\nThe dollar sign `pattern:$` behaves similarly.\n\nThe regular expression `pattern:\\d$` finds the last digit in every line\n\n```js run\nlet str = `Winnie: 1\nPiglet: 2\nEeyore: 3`;\n\nalert( str.match(/\\d$/gm) ); // 1,2,3\n```\n\nWithout the flag `pattern:m`, the dollar `pattern:$` would only match the end of the whole text, so only the very last digit would be found.\n\n```smart\n\"End of a line\" formally means \"immediately before a line break\": the test  `pattern:$` in multiline mode matches at all positions succeeded by a newline character `\\n`.\n\nAnd at the text end.\n```\n\n## Searching for \\n instead of ^ $\n\nTo find a newline, we can use not only anchors `pattern:^` and `pattern:$`, but also the newline character `\\n`.\n\nWhat's the difference? Let's see an example.\n\nHere we search for `pattern:\\d\\n` instead of `pattern:\\d$`:\n\n```js run\nlet str = `Winnie: 1\nPiglet: 2\nEeyore: 3`;\n\nalert( str.match(/\\d\\n/gm) ); // 1\\n,2\\n\n```\n\nAs we can see, there are 2 matches instead of 3.\n\nThat's because there's no newline after `subject:3` (there's text end though, so it matches `pattern:$`).\n\nAnother difference: now every match includes a newline character `match:\\n`. Unlike the anchors `pattern:^` `pattern:$`, that only test the condition (start/end of a line), `\\n` is a character, so it becomes a part of the result.\n\nSo, a `\\n` in the pattern is used when we need newline characters in the result, while anchors are used to find something at the beginning/end of a line.\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md",
    "content": "\nThe answer: `pattern:\\b\\d\\d:\\d\\d\\b`.\n\n```js run\nalert( \"Breakfast at 09:00 in the room 123:456.\".match( /\\b\\d\\d:\\d\\d\\b/ ) ); // 09:00\n```\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md",
    "content": "# Find the time\n\nThe time has a format: `hours:minutes`. Both hours and minutes has two digits, like `09:00`.\n\nMake a regexp to find time in the string: `subject:Breakfast at 09:00 in the room 123:456.`\n\nP.S. In this task there's no need to check time correctness yet, so `25:99` can also be a valid result.\n\nP.P.S. The regexp shouldn't match `123:456`.\n"
  },
  {
    "path": "9-regular-expressions/06-regexp-boundary/article.md",
    "content": "# Word boundary: \\b\n\nA word boundary `pattern:\\b` is a test, just like `pattern:^` and `pattern:$`.\n\nWhen the regexp engine (program module that implements searching for regexps) comes across `pattern:\\b`, it checks that the position in the string is a word boundary.\n\nThere are three different positions that qualify as word boundaries:\n\n- At string start, if the first string character is a word character `pattern:\\w`.\n- Between two characters in the string, where one is a word character `pattern:\\w` and the other is not.\n- At string end, if the last string character is a word character `pattern:\\w`.\n\nFor instance, regexp `pattern:\\bJava\\b` will be found in `subject:Hello, Java!`, where `subject:Java` is a standalone word, but not in `subject:Hello, JavaScript!`.\n\n```js run\nalert( \"Hello, Java!\".match(/\\bJava\\b/) ); // Java\nalert( \"Hello, JavaScript!\".match(/\\bJava\\b/) ); // null\n```\n\nIn the string `subject:Hello, Java!` following positions correspond to `pattern:\\b`:\n\n![](hello-java-boundaries.svg)\n\nSo, it matches the pattern `pattern:\\bHello\\b`, because:\n\n1. At the beginning of the string matches the first test `pattern:\\b`.\n2. Then matches the word `pattern:Hello`.\n3. Then the test `pattern:\\b` matches again, as we're between `subject:o` and a comma.\n\nSo the pattern `pattern:\\bHello\\b` would match, but not `pattern:\\bHell\\b` (because there's no word boundary after `l`) and not `Java!\\b` (because the exclamation sign is not a wordly character `pattern:\\w`, so there's no word boundary after it).\n\n```js run\nalert( \"Hello, Java!\".match(/\\bHello\\b/) ); // Hello\nalert( \"Hello, Java!\".match(/\\bJava\\b/) );  // Java\nalert( \"Hello, Java!\".match(/\\bHell\\b/) );  // null (no match)\nalert( \"Hello, Java!\".match(/\\bJava!\\b/) ); // null (no match)\n```\n\nWe can use `pattern:\\b` not only with words, but with digits as well.\n\nFor example, the pattern `pattern:\\b\\d\\d\\b` looks for standalone 2-digit numbers. In other words, it looks for 2-digit numbers that are surrounded by characters different from `pattern:\\w`, such as spaces or punctuation (or text start/end).\n\n```js run\nalert( \"1 23 456 78\".match(/\\b\\d\\d\\b/g) ); // 23,78\nalert( \"12,34,56\".match(/\\b\\d\\d\\b/g) ); // 12,34,56\n```\n\n```warn header=\"Word boundary `pattern:\\b` doesn't work for non-latin alphabets\"\nThe word boundary test `pattern:\\b` checks that there should be `pattern:\\w` on the one side from the position and \"not `pattern:\\w`\" - on the other side.\n\nBut `pattern:\\w` means a latin letter `a-z` (or a digit or an underscore), so the test doesn't work for other characters, e.g. cyrillic letters or hieroglyphs.\n```\n"
  },
  {
    "path": "9-regular-expressions/07-regexp-escaping/article.md",
    "content": "\n# Escaping, special characters\n\nAs we've seen, a backslash `pattern:\\` is used to denote character classes, e.g. `pattern:\\d`. So it's a special character in regexps (just like in regular strings).\n\nThere are other special characters as well, that have special meaning in a regexp. They are used to do more powerful searches. Here's a full list of them: `pattern:[ \\ ^ $ . | ? * + ( )`.\n\nDon't try to remember the list -- soon we'll deal with each of them separately and you'll know them by heart automatically.\n\n## Escaping\n\nLet's say we want to find literally a dot. Not \"any character\", but just a dot.\n\nTo use a special character as a regular one, prepend it with a backslash: `pattern:\\.`.\n\nThat's also called \"escaping a character\".\n\nFor example:\n```js run\nalert( \"Chapter 5.1\".match(/\\d\\.\\d/) ); // 5.1 (match!)\nalert( \"Chapter 511\".match(/\\d\\.\\d/) ); // null (looking for a real dot \\.)\n```\n\nParentheses are also special characters, so if we want them, we should use `pattern:\\(`. The example below looks for a string `\"g()\"`:\n\n```js run\nalert( \"function g()\".match(/g\\(\\)/) ); // \"g()\"\n```\n\nIf we're looking for a backslash `\\`, it's a special character in both regular strings and regexps, so we should double it.\n\n```js run\nalert( \"1\\\\2\".match(/\\\\/) ); // '\\'\n```\n\n## A slash\n\nA slash symbol `'/'` is not a special character, but in JavaScript it is used to open and close the regexp: `pattern:/...pattern.../`, so we should escape it too.\n\nHere's what a search for a slash `'/'` looks like:\n\n```js run\nalert( \"/\".match(/\\//) ); // '/'\n```\n\nOn the other hand, if we're not using `pattern:/.../`, but create a regexp using `new RegExp`, then we don't need to escape it:\n\n```js run\nalert( \"/\".match(new RegExp(\"/\")) ); // finds /\n```\n\n## new RegExp\n\nIf we are creating a regular expression with `new RegExp`, then we don't have to escape `/`, but need to do some other escaping.\n\nFor instance, consider this:\n\n```js run\nlet regexp = new RegExp(\"\\d\\.\\d\");\n\nalert( \"Chapter 5.1\".match(regexp) ); // null\n```\n\nThe similar search in one of previous examples worked with `pattern:/\\d\\.\\d/`, but `new RegExp(\"\\d\\.\\d\")` doesn't work, why?\n\nThe reason is that backslashes are \"consumed\" by a string. As we may recall, regular strings have their own special characters, such as `\\n`, and a backslash is used for escaping.\n\nHere's how \"\\d\\.\\d\" is perceived:\n\n```js run\nalert(\"\\d\\.\\d\"); // d.d\n```\n\nString quotes \"consume\" backslashes and interpret them on their own, for instance:\n\n- `\\n` -- becomes a newline character,\n- `\\u1234` -- becomes the Unicode character with such code,\n- ...And when there's no special meaning: like `pattern:\\d` or `\\z`, then the backslash is simply removed.\n\nSo `new RegExp` gets a string without backslashes. That's why the search doesn't work!\n\nTo fix it, we need to double backslashes, because string quotes turn `\\\\` into `\\`:\n\n```js run\n*!*\nlet regStr = \"\\\\d\\\\.\\\\d\";\n*/!*\nalert(regStr); // \\d\\.\\d (correct now)\n\nlet regexp = new RegExp(regStr);\n\nalert( \"Chapter 5.1\".match(regexp) ); // 5.1\n```\n\n## Summary\n\n- To search for special characters `pattern:[ \\ ^ $ . | ? * + ( )` literally, we need to prepend them with a backslash `\\` (\"escape them\").\n- We also need to escape `/` if we're inside `pattern:/.../` (but not inside `new RegExp`).\n- When passing a string to `new RegExp`, we need to double backslashes `\\\\`, cause string quotes consume one of them.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md",
    "content": "Answers: **no, yes**.\n\n- In the script `subject:Java` it doesn't match anything, because `pattern:[^script]` means \"any character except given ones\". So the regexp looks for `\"Java\"` followed by one such symbol, but there's a string end, no symbols after it.\n\n    ```js run\n    alert( \"Java\".match(/Java[^script]/) ); // null\n    ```\n- Yes, because the `pattern:[^script]` part matches the character `\"S\"`. It's not one of `pattern:script`. As the regexp is case-sensitive (no `pattern:i` flag), it treats `\"S\"` as a different character from `\"s\"`.\n\n    ```js run\n    alert( \"JavaScript\".match(/Java[^script]/) ); // \"JavaS\"\n    ```\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md",
    "content": "# Java[^script]\n\nWe have a regexp `pattern:/Java[^script]/`.\n\nDoes it match anything in the string `subject:Java`? In the string `subject:JavaScript`?\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md",
    "content": "Answer: `pattern:\\d\\d[-:]\\d\\d`.\n\n```js run\nlet regexp = /\\d\\d[-:]\\d\\d/g;\nalert( \"Breakfast at 09:00. Dinner at 21-30\".match(regexp) ); // 09:00, 21-30\n```\n\nPlease note that the dash `pattern:'-'` has a special meaning in square brackets, but only between other characters, not when it's in the beginning or at the end, so we don't need to escape it.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md",
    "content": "# Find the time as hh:mm or hh-mm\n\nThe time can be in the format `hours:minutes` or `hours-minutes`. Both hours and minutes have 2 digits:  `09:00` or `21-30`.\n\nWrite a regexp to find time:\n\n```js\nlet regexp = /your regexp/g;\nalert( \"Breakfast at 09:00. Dinner at 21-30\".match(regexp) ); // 09:00, 21-30\n```\n\nP.S. In this task we assume that the time is always correct, there's no need to filter out bad strings like \"45:67\". Later we'll deal with that too.\n"
  },
  {
    "path": "9-regular-expressions/08-regexp-character-sets-and-ranges/article.md",
    "content": "# Sets and ranges [...]\n\nSeveral characters or character classes inside square brackets `[…]` mean to \"search for any character among given\".\n\n## Sets\n\nFor instance, `pattern:[eao]` means any of the 3 characters: `'a'`, `'e'`, or `'o'`.\n\nThat's called a *set*. Sets can be used in a regexp along with regular characters:\n\n```js run\n// find [t or m], and then \"op\"\nalert( \"Mop top\".match(/[tm]op/gi) ); // \"Mop\", \"top\"\n```\n\nPlease note that although there are multiple characters in the set, they correspond to exactly one character in the match.\n\nSo the example below gives no matches:\n\n```js run\n// find \"V\", then [o or i], then \"la\"\nalert( \"Voila\".match(/V[oi]la/) ); // null, no matches\n```\n\nThe pattern searches for:\n\n- `pattern:V`,\n- then *one* of the letters `pattern:[oi]`,\n- then `pattern:la`.\n\nSo there would be a match for `match:Vola` or `match:Vila`.\n\n## Ranges\n\nSquare brackets may also contain *character ranges*.\n\nFor instance, `pattern:[a-z]` is a character in range from `a` to `z`, and `pattern:[0-5]` is a digit from `0` to `5`.\n\nIn the example below we're searching for `\"x\"` followed by two digits or letters from `A` to `F`:\n\n```js run\nalert( \"Exception 0xAF\".match(/x[0-9A-F][0-9A-F]/g) ); // xAF\n```\n\nHere `pattern:[0-9A-F]` has two ranges: it searches for a character that is either a digit from `0` to `9` or a letter from `A` to `F`.\n\nIf we'd like to look for lowercase letters as well, we can add the range `a-f`: `pattern:[0-9A-Fa-f]`. Or add the flag `pattern:i`.\n\nWe can also use character classes inside `[…]`.\n\nFor instance, if we'd like to look for a wordly character `pattern:\\w` or a hyphen `pattern:-`, then the set is `pattern:[\\w-]`.\n\nCombining multiple classes is also possible, e.g. `pattern:[\\s\\d]` means \"a space character or a digit\".\n\n```smart header=\"Character classes are shorthands for certain character sets\"\nFor instance:\n\n- **\\d** -- is the same as `pattern:[0-9]`,\n- **\\w** -- is the same as `pattern:[a-zA-Z0-9_]`,\n- **\\s** -- is the same as `pattern:[\\t\\n\\v\\f\\r ]`, plus few other rare Unicode space characters.\n```\n\n### Example: multi-language \\w\n\nAs the character class `pattern:\\w` is a shorthand for `pattern:[a-zA-Z0-9_]`, it can't find Chinese hieroglyphs, Cyrillic letters, etc.\n\nWe can write a more universal pattern, that looks for wordly characters in any language. That's easy with Unicode properties: `pattern:[\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]`.\n\nLet's decipher it. Similar to `pattern:\\w`, we're making a set of our own that includes characters with following Unicode properties:\n\n- `Alphabetic` (`Alpha`) - for letters,\n- `Mark` (`M`) - for accents,\n- `Decimal_Number` (`Nd`) - for digits,\n- `Connector_Punctuation` (`Pc`) - for the underscore `'_'` and similar characters,\n- `Join_Control` (`Join_C`) - two special codes `200c` and `200d`, used in ligatures, e.g. in Arabic.\n\nAn example of use:\n\n```js run\nlet regexp = /[\\p{Alpha}\\p{M}\\p{Nd}\\p{Pc}\\p{Join_C}]/gu;\n\nlet str = `Hi 你好 12`;\n\n// finds all letters and digits:\nalert( str.match(regexp) ); // H,i,你,好,1,2\n```\n\nOf course, we can edit this pattern: add Unicode properties or remove them. Unicode properties are covered in more details in the article <info:regexp-unicode>.\n\n```warn header=\"Unicode properties aren't supported in IE\"\nUnicode properties `pattern:p{…}` are not implemented in IE. If we really need them, we can use library [XRegExp](http://xregexp.com/).\n\nOr just use ranges of characters in a language that interests us, e.g.  `pattern:[а-я]` for Cyrillic letters.\n```\n\n## Excluding ranges\n\nBesides normal ranges, there are \"excluding\" ranges that look like `pattern:[^…]`.\n\nThey are denoted by a caret character `^` at the start and match any character *except the given ones*.\n\nFor instance:\n\n- `pattern:[^aeyo]` -- any character except  `'a'`, `'e'`, `'y'` or `'o'`.\n- `pattern:[^0-9]` -- any character except a digit, the same as `pattern:\\D`.\n- `pattern:[^\\s]` -- any non-space character, same as `\\S`.\n\nThe example below looks for any characters except letters, digits and spaces:\n\n```js run\nalert( \"alice15@gmail.com\".match(/[^\\d\\sA-Z]/gi) ); // @ and .\n```\n\n## Escaping in […]\n\nUsually when we want to find exactly a special character, we need to escape it like `pattern:\\.`. And if we need a backslash, then we use `pattern:\\\\`, and so on.\n\nIn square brackets we can use the vast majority of special characters without escaping:\n\n- Symbols `pattern:. + ( )` never need escaping.\n- A hyphen `pattern:-` is not escaped in the beginning or the end (where it does not define a range).\n- A caret `pattern:^` is only escaped in the beginning (where it means exclusion).\n- The closing square bracket `pattern:]` is always escaped (if we need to look for that symbol).\n\nIn other words, all special characters are allowed without escaping, except when they mean something for square brackets.\n\nA dot `.` inside square brackets means just a dot. The pattern `pattern:[.,]` would look for one of characters: either a dot or a comma.\n\nIn the example below the regexp `pattern:[-().^+]` looks for one of the characters `-().^+`:\n\n```js run\n// No need to escape\nlet regexp = /[-().^+]/g;\n\nalert( \"1 + 2 - 3\".match(regexp) ); // Matches +, -\n```\n\n...But if you decide to escape them \"just in case\", then there would be no harm:\n\n```js run\n// Escaped everything\nlet regexp = /[\\-\\(\\)\\.\\^\\+]/g;\n\nalert( \"1 + 2 - 3\".match(regexp) ); // also works: +, -\n```\n\n## Ranges and flag \"u\"\n\nIf there are surrogate pairs in the set, flag `pattern:u` is required for them to work correctly.\n\nFor instance, let's look for `pattern:[𝒳𝒴]` in the string `subject:𝒳`:\n\n```js run\nalert( '𝒳'.match(/[𝒳𝒴]/) ); // shows a strange character, like [?]\n// (the search was performed incorrectly, half-character returned)\n```\n\nThe result is incorrect, because by default regular expressions \"don't know\" about surrogate pairs.\n\nThe regular expression engine thinks that `[𝒳𝒴]` -- are not two, but four characters:\n1. left half of `𝒳` `(1)`,\n2. right half of `𝒳` `(2)`,\n3. left half of `𝒴` `(3)`,\n4. right half of `𝒴` `(4)`.\n\nWe can see their codes like this:\n\n```js run\nfor(let i=0; i<'𝒳𝒴'.length; i++) {\n  alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500\n};\n```\n\nSo, the example above finds and shows the left half of `𝒳`.\n\nIf we add flag `pattern:u`, then the behavior will be correct:\n\n```js run\nalert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳\n```\n\nThe similar situation occurs when looking for a range, such as `[𝒳-𝒴]`.\n\nIf we forget to add flag `pattern:u`, there will be an error:\n\n```js run\n'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression\n```\n\nThe reason is that without flag `pattern:u` surrogate pairs are perceived as two characters, so `[𝒳-𝒴]` is interpreted as `[<55349><56499>-<55349><56500>]` (every surrogate pair is replaced with its codes). Now it's easy to see that the range `56499-55349` is invalid: its starting code `56499` is greater than the end `55349`. That's the formal reason for the error.\n\nWith the flag `pattern:u` the pattern works correctly:\n\n```js run\n// look for characters from 𝒳 to 𝒵\nalert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md",
    "content": "\nSolution:\n\n```js run\nlet regexp = /\\.{3,}/g;\nalert( \"Hello!... How goes?.....\".match(regexp) ); // ..., .....\n```\n\nPlease note that the dot is a special character, so we have to escape it and insert as `\\.`.\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md",
    "content": "importance: 5\n\n---\n\n#  How to find an ellipsis \"...\" ?\n\nCreate a regexp to find ellipsis: 3 (or more?) dots in a row.\n\nCheck it:\n\n```js\nlet regexp = /your regexp/g;\nalert( \"Hello!... How goes?.....\".match(regexp) ); // ..., .....\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md",
    "content": "We need to look for `#` followed by 6 hexadecimal characters.\n\nA hexadecimal character can be described as `pattern:[0-9a-fA-F]`. Or if we use the `pattern:i` flag, then just  `pattern:[0-9a-f]`.\n\nThen we can look for 6 of them using the quantifier `pattern:{6}`.\n\nAs a result, we have the regexp: `pattern:/#[a-f0-9]{6}/gi`.\n\n```js run\nlet regexp = /#[a-f0-9]{6}/gi;\n\nlet str = \"color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2\"\n\nalert( str.match(regexp) );  // #121212,#AA00ef\n```\n\nThe problem is that it finds the color in longer sequences:\n\n```js run\nalert( \"#12345678\".match( /#[a-f0-9]{6}/gi ) ) // #123456\n```\n\nTo fix that, we can add `pattern:\\b` to the end:\n\n```js run\n// color\nalert( \"#123456\".match( /#[a-f0-9]{6}\\b/gi ) ); // #123456\n\n// not a color\nalert( \"#12345678\".match( /#[a-f0-9]{6}\\b/gi ) ); // null\n```\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md",
    "content": "# Regexp for HTML colors\n\nCreate a regexp to search HTML-colors written as `#ABCDEF`: first `#` and then 6 hexadecimal characters.\n\nAn example of use:\n\n```js\nlet regexp = /...your regexp.../\n\nlet str = \"color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678\";\n\nalert( str.match(regexp) )  // #121212,#AA00ef\n```\n\nP.S. In this task we do not need other color formats like `#123` or `rgb(1,2,3)` etc.\n"
  },
  {
    "path": "9-regular-expressions/09-regexp-quantifiers/article.md",
    "content": "# Quantifiers +, *, ? and {n}\n\nLet's say we have a string like `+7(903)-123-45-67` and want to find all numbers in it. But unlike before, we are interested not in single digits, but full numbers: `7, 903, 123, 45, 67`.\n\nA number is a sequence of 1 or more digits `pattern:\\d`. To mark how many we need, we can append a *quantifier*.\n\n## Quantity {n}\n\nThe simplest quantifier is a number in curly braces: `pattern:{n}`.\n\nA quantifier is appended to a character (or a character class, or a `[...]` set etc) and specifies how many we need.\n\nIt has a few advanced forms, let's see examples:\n\nThe exact count: `pattern:{5}`\n: `pattern:\\d{5}` denotes exactly 5 digits, the same as `pattern:\\d\\d\\d\\d\\d`.\n\n    The example below looks for a 5-digit number:\n\n    ```js run\n    alert( \"I'm 12345 years old\".match(/\\d{5}/) ); //  \"12345\"\n    ```\n\n    We can add `\\b` to exclude longer numbers: `pattern:\\b\\d{5}\\b`.\n\nThe range: `pattern:{3,5}`, match 3-5 times\n: To find numbers from 3 to 5 digits we can put the limits into curly braces: `pattern:\\d{3,5}`\n\n    ```js run\n    alert( \"I'm not 12, but 1234 years old\".match(/\\d{3,5}/) ); // \"1234\"\n    ```\n\n    We can omit the upper limit.\n\n    Then a regexp `pattern:\\d{3,}` looks for sequences of digits of length `3` or more:\n\n    ```js run\n    alert( \"I'm not 12, but 345678 years old\".match(/\\d{3,}/) ); // \"345678\"\n    ```\n\nLet's return to the string `+7(903)-123-45-67`.\n\nA number is a sequence of one or more digits in a row. So the regexp is `pattern:\\d{1,}`:\n\n```js run\nlet str = \"+7(903)-123-45-67\";\n\nlet numbers = str.match(/\\d{1,}/g);\n\nalert(numbers); // 7,903,123,45,67\n```\n\n## Shorthands\n\nThere are shorthands for most used quantifiers:\n\n`pattern:+`\n: Means \"one or more\", the same as `pattern:{1,}`.\n\n    For instance, `pattern:\\d+` looks for numbers:\n\n    ```js run\n    let str = \"+7(903)-123-45-67\";\n\n    alert( str.match(/\\d+/g) ); // 7,903,123,45,67\n    ```\n\n`pattern:?`\n: Means \"zero or one\", the same as `pattern:{0,1}`. In other words, it makes the symbol optional.\n\n    For instance, the pattern `pattern:ou?r` looks for `match:o` followed by zero or one `match:u`, and then `match:r`.\n\n    So, `pattern:colou?r` finds both `match:color` and `match:colour`:\n\n    ```js run\n    let str = \"Should I write color or colour?\";\n\n    alert( str.match(/colou?r/g) ); // color, colour\n    ```\n\n`pattern:*`\n: Means \"zero or more\", the same as `pattern:{0,}`. That is, the character may repeat any times or be absent.\n\n    For example, `pattern:\\d0*` looks for a digit followed by any number of zeroes (may be many or none):\n\n    ```js run\n    alert( \"100 10 1\".match(/\\d0*/g) ); // 100, 10, 1\n    ```\n\n    Compare it with `pattern:+` (one or more):\n\n    ```js run\n    alert( \"100 10 1\".match(/\\d0+/g) ); // 100, 10\n    // 1 not matched, as 0+ requires at least one zero\n    ```\n\n## More examples\n\nQuantifiers are used very often. They serve as the main \"building block\" of complex regular expressions, so let's see more examples.\n\n**Regexp for decimal fractions (a number with a floating point): `pattern:\\d+\\.\\d+`**\n\nIn action:\n```js run\nalert( \"0 1 12.345 7890\".match(/\\d+\\.\\d+/g) ); // 12.345\n```\n\n**Regexp for an \"opening HTML-tag without attributes\", such as `<span>` or `<p>`.**\n\n1. The simplest one: `pattern:/<[a-z]+>/i`\n\n    ```js run\n    alert( \"<body> ... </body>\".match(/<[a-z]+>/gi) ); // <body>\n    ```\n\n    The regexp looks for character `pattern:'<'` followed by one or more Latin letters, and then  `pattern:'>'`.\n\n2. Improved: `pattern:/<[a-z][a-z0-9]*>/i`\n\n    According to the standard, HTML tag name may have a digit at any position except the first one, like `<h1>`.\n\n    ```js run\n    alert( \"<h1>Hi!</h1>\".match(/<[a-z][a-z0-9]*>/gi) ); // <h1>\n    ```\n\n**Regexp \"opening or closing HTML-tag without attributes\": `pattern:/<\\/?[a-z][a-z0-9]*>/i`**\n\nWe added an optional slash `pattern:/?` near the beginning of the pattern. Had to escape it with a backslash, otherwise JavaScript would think it is the pattern end.\n\n```js run\nalert( \"<h1>Hi!</h1>\".match(/<\\/?[a-z][a-z0-9]*>/gi) ); // <h1>, </h1>\n```\n\n```smart header=\"To make a regexp more precise, we often need make it more complex\"\nWe can see one common rule in these examples: the more precise is the regular expression -- the longer and more complex it is.\n\nFor instance, for HTML tags we could use a simpler regexp: `pattern:<\\w+>`. But as HTML has stricter restrictions for a tag name, `pattern:<[a-z][a-z0-9]*>` is more reliable.\n\nCan we use `pattern:<\\w+>` or we need `pattern:<[a-z][a-z0-9]*>`?\n\nIn real life both variants are acceptable. Depends on how tolerant we can be to \"extra\" matches and whether it's difficult or not to remove them from the result by other means.\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md",
    "content": "\nThe result is: `match:123 4`.\n\nFirst the lazy `pattern:\\d+?` tries to take as little digits as it can, but it has to reach the space, so it takes  `match:123`.\n\nThen the second `\\d+?` takes only one digit, because that's enough.\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md",
    "content": "# A match for /d+? d+?/\n\nWhat's the match here?\n\n```js\nalert( \"123 456\".match(/\\d+? \\d+?/g) ); // ?\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md",
    "content": "We need to find the beginning of the comment `match:<!--`, then everything till the end of `match:-->`.\n\nAn acceptable variant is `pattern:<!--.*?-->` -- the lazy quantifier makes the dot stop right before `match:-->`. We also need to add flag `pattern:s` for the dot to include newlines.\n\nOtherwise multiline comments won't be found:\n\n```js run\nlet regexp = /<!--.*?-->/gs;\n\nlet str = `... <!-- My -- comment\n test --> ..  <!----> ..\n`;\n\nalert( str.match(regexp) ); // '<!-- My -- comment \\n test -->', '<!---->'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md",
    "content": "# Find HTML comments\n\nFind all HTML comments in the text:\n\n```js\nlet regexp = /your regexp/g;\n\nlet str = `... <!-- My -- comment\n test --> ..  <!----> .. \n`;\n\nalert( str.match(regexp) ); // '<!-- My -- comment \\n test -->', '<!---->'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md",
    "content": "\nThe solution is `pattern:<[^<>]+>`.\n\n```js run\nlet regexp = /<[^<>]+>/g;\n\nlet str = '<> <a href=\"/\"> <input type=\"radio\" checked> <b>';\n\nalert( str.match(regexp) ); // '<a href=\"/\">', '<input type=\"radio\" checked>', '<b>'\n```\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md",
    "content": "# Find HTML tags\n\nCreate a regular expression to find all (opening and closing) HTML tags with their attributes.\n\nAn example of use:\n\n```js run\nlet regexp = /your regexp/g;\n\nlet str = '<> <a href=\"/\"> <input type=\"radio\" checked> <b>';\n\nalert( str.match(regexp) ); // '<a href=\"/\">', '<input type=\"radio\" checked>', '<b>'\n```\n\nHere we assume that tag attributes may not contain `<` and `>` (inside quotes too), that simplifies things a bit.\n"
  },
  {
    "path": "9-regular-expressions/10-regexp-greedy-and-lazy/article.md",
    "content": "# Greedy and lazy quantifiers\n\nQuantifiers are very simple from the first sight, but in fact they can be tricky.\n\nWe should understand how the search works very well if we plan to look for something more complex than `pattern:/\\d+/`.\n\nLet's take the following task as an example.\n\nWe have a text and need to replace all quotes `\"...\"` with guillemet marks: `«...»`. They are preferred for typography in many countries.\n\nFor instance: `\"Hello, world\"` should become `«Hello, world»`. There exist other quotes, such as `„Witam, świat!”` (Polish) or `「你好，世界」` (Chinese), but for our task let's choose `«...»`.\n\nThe first thing to do is to locate quoted strings, and then we can replace them.\n\nA regular expression like `pattern:/\".+\"/g` (a quote, then something, then the other quote) may seem like a good fit, but it isn't!\n\nLet's try it:\n\n```js run\nlet regexp = /\".+\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\" and her \"broom\"\n```\n\n...We can see that it works not as intended!\n\nInstead of finding two matches `match:\"witch\"` and `match:\"broom\"`, it finds one: `match:\"witch\" and her \"broom\"`.\n\nThat can be described as \"greediness is the cause of all evil\".\n\n## Greedy search\n\nTo find a match, the regular expression engine uses the following algorithm:\n\n- For every position in the string\n    - Try to match the pattern at that position.\n    - If there's no match, go to the next position.\n\nThese common words do not make it obvious why the regexp fails, so let's elaborate how the search works for the pattern `pattern:\".+\"`.\n\n1. The first pattern character is a quote `pattern:\"`.\n\n    The regular expression engine tries to find it at the zero position of the source string `subject:a \"witch\" and her \"broom\" is one`, but there's `subject:a` there, so there's immediately no match.\n\n    Then it advances: goes to the next positions in the source string and tries to find the first character of the pattern there, fails again, and finally finds the quote at the 3rd position:\n\n    ![](witch_greedy1.svg)\n\n2. The quote is detected, and then the engine tries to find a match for the rest of the pattern. It tries to see if the rest of the subject string conforms to `pattern:.+\"`.\n\n    In our case the next pattern character is `pattern:.` (a dot). It denotes \"any character except a newline\", so the next string letter `match:'w'` fits:\n\n    ![](witch_greedy2.svg)\n\n3. Then the dot repeats because of the quantifier `pattern:.+`. The regular expression engine adds to the match one character after another.\n\n    ...Until when? All characters match the dot, so it only stops when it reaches the end of the string:\n\n    ![](witch_greedy3.svg)\n\n4. Now the engine finished repeating `pattern:.+` and tries to find the next character of the pattern. It's the quote `pattern:\"`. But there's a problem: the string has finished, there are no more characters!\n\n    The regular expression engine understands that it took too many `pattern:.+` and starts to *backtrack*.\n\n    In other words, it shortens the match for the quantifier by one character:\n\n    ![](witch_greedy4.svg)\n\n    Now it assumes that `pattern:.+` ends one character before the string end and tries to match the rest of the pattern from that position.\n\n    If there were a quote there, then the search would end, but the last character is `subject:'e'`, so there's no match.\n\n5. ...So the engine decreases the number of repetitions of `pattern:.+` by one more character:\n\n    ![](witch_greedy5.svg)\n\n    The quote `pattern:'\"'` does not match `subject:'n'`.\n\n6. The engine keep backtracking: it decreases the count of repetition for `pattern:'.'` until the rest of the pattern (in our case `pattern:'\"'`) matches:\n\n    ![](witch_greedy6.svg)\n\n7. The match is complete.\n\n8. So the first match is `match:\"witch\" and her \"broom\"`. If the regular expression has flag `pattern:g`, then the search will continue from where the first match ends. There are no more quotes in the rest of the string `subject:is one`, so no more results.\n\nThat's probably not what we expected, but that's how it works.\n\n**In the greedy mode (by default) a quantified character is repeated as many times as possible.**\n\nThe regexp engine adds to the match as many characters as it can for `pattern:.+`, and then shortens that one by one, if the rest of the pattern doesn't match.\n\nFor our task we want another thing. That's where a lazy mode can help.\n\n## Lazy mode\n\nThe lazy mode of quantifiers is an opposite to the greedy mode. It means: \"repeat minimal number of times\".\n\nWe can enable it by putting a question mark `pattern:'?'` after the quantifier, so that it becomes  `pattern:*?` or `pattern:+?` or even `pattern:??` for `pattern:'?'`.\n\nTo make things clear: usually a question mark `pattern:?` is a quantifier by itself (zero or one), but if added *after another quantifier (or even itself)* it gets another meaning -- it switches the matching mode from greedy to lazy.\n\nThe regexp `pattern:/\".+?\"/g` works as intended: it finds `match:\"witch\"` and `match:\"broom\"`:\n\n```js run\nlet regexp = /\".+?\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\", \"broom\"\n```\n\nTo clearly understand the change, let's trace the search step by step.\n\n1. The first step is the same: it finds the pattern start `pattern:'\"'` at the 3rd position:\n\n    ![](witch_greedy1.svg)\n\n2. The next step is also similar: the engine finds a match for the dot `pattern:'.'`:\n\n    ![](witch_greedy2.svg)\n\n3. And now the search goes differently. Because we have a lazy mode for `pattern:+?`, the engine doesn't try to match a dot one more time, but stops and tries to match the rest of the pattern  `pattern:'\"'` right now:\n\n    ![](witch_lazy3.svg)\n\n    If there were a quote there, then the search would end, but there's `'i'`, so there's no match.\n4. Then the regular expression engine increases the number of repetitions for the dot and tries one more time:\n\n    ![](witch_lazy4.svg)\n\n    Failure again. Then the number of repetitions is increased again and again...\n5. ...Till the match for the rest of the pattern is found:\n\n    ![](witch_lazy5.svg)\n\n6. The next search starts from the end of the current match and yield one more result:\n\n    ![](witch_lazy6.svg)\n\nIn this example we saw how the lazy mode works for `pattern:+?`. Quantifiers `pattern:*?` and `pattern:??` work the similar way -- the regexp engine increases the number of repetitions only if the rest of the pattern can't match on the given position.\n\n**Laziness is only enabled for the quantifier with `?`.**\n\nOther quantifiers remain greedy.\n\nFor instance:\n\n```js run\nalert( \"123 456\".match(/\\d+ \\d+?/) ); // 123 4\n```\n\n1. The pattern `pattern:\\d+` tries to match as many digits as it can (greedy mode), so it finds  `match:123` and stops, because the next character is a space `pattern:' '`.\n2. Then there's a space in the pattern, it matches.\n3. Then there's `pattern:\\d+?`. The quantifier is in lazy mode, so it finds one digit `match:4` and tries to check if the rest of the pattern matches from there.\n\n    ...But there's nothing in the pattern after `pattern:\\d+?`.\n\n    The lazy mode doesn't repeat anything without a need. The pattern finished, so we're done. We have a match `match:123 4`.\n\n```smart header=\"Optimizations\"\nModern regular expression engines can optimize internal algorithms to work faster. So they may work a bit differently from the described algorithm.\n\nBut to understand how regular expressions work and to build regular expressions, we don't need to know about that. They are only used internally to optimize things.\n\nComplex regular expressions are hard to optimize, so the search may work exactly as described as well.\n```\n\n## Alternative approach\n\nWith regexps, there's often more than one way to do the same thing.\n\nIn our case we can find quoted strings without lazy mode using the regexp `pattern:\"[^\"]+\"`:\n\n```js run\nlet regexp = /\"[^\"]+\"/g;\n\nlet str = 'a \"witch\" and her \"broom\" is one';\n\nalert( str.match(regexp) ); // \"witch\", \"broom\"\n```\n\nThe regexp `pattern:\"[^\"]+\"` gives correct results, because it looks for a quote `pattern:'\"'` followed by one or more non-quotes `pattern:[^\"]`, and then the closing quote.\n\nWhen the regexp engine looks for `pattern:[^\"]+` it stops the repetitions when it meets the closing quote, and we're done.\n\nPlease note, that this logic does not replace lazy quantifiers!\n\nIt is just different. There are times when we need one or another.\n\n**Let's see an example where lazy quantifiers fail and this variant works right.**\n\nFor instance, we want to find links of the form `<a href=\"...\" class=\"doc\">`, with any `href`.\n\nWhich regular expression to use?\n\nThe first idea might be: `pattern:/<a href=\".*\" class=\"doc\">/g`.\n\nLet's check it:\n```js run\nlet str = '...<a href=\"link\" class=\"doc\">...';\nlet regexp = /<a href=\".*\" class=\"doc\">/g;\n\n// Works!\nalert( str.match(regexp) ); // <a href=\"link\" class=\"doc\">\n```\n\nIt worked. But let's see what happens if there are many links in the text?\n\n```js run\nlet str = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\".*\" class=\"doc\">/g;\n\n// Whoops! Two links in one match!\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\nNow the result is wrong for the same reason as our \"witches\" example. The quantifier `pattern:.*` took too many characters.\n\nThe match looks like this:\n\n```html\n<a href=\".....................................\" class=\"doc\">\n<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\nLet's modify the pattern by making the quantifier `pattern:.*?` lazy:\n\n```js run\nlet str = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\".*?\" class=\"doc\">/g;\n\n// Works!\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"doc\">, <a href=\"link2\" class=\"doc\">\n```\n\nNow it seems to work, there are two matches:\n\n```html\n<a href=\".....\" class=\"doc\">    <a href=\".....\" class=\"doc\">\n<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">\n```\n\n...But let's test it on one more text input:\n\n```js run\nlet str = '...<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">...';\nlet regexp = /<a href=\".*?\" class=\"doc\">/g;\n\n// Wrong match!\nalert( str.match(regexp) ); // <a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">\n```\n\nNow it fails. The match includes not just a link, but also a lot of text after it, including `<p...>`.\n\nWhy?\n\nThat's what's going on:\n\n1. First the regexp finds a link start `match:<a href=\"`.\n2. Then it looks for `pattern:.*?`: takes one character (lazily!), check if there's a match for `pattern:\" class=\"doc\">` (none).\n3. Then takes another character into `pattern:.*?`, and so on... until it finally reaches `match:\" class=\"doc\">`.\n\nBut the problem is: that's already beyond the link `<a...>`, in another tag `<p>`. Not what we want.\n\nHere's the picture of the match aligned with the text:\n\n```html\n<a href=\"...................................\" class=\"doc\">\n<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">\n```\n\nSo, we need the pattern to look for `<a href=\"...something...\" class=\"doc\">`, but both greedy and lazy variants have problems.\n\nThe correct variant can be: `pattern:href=\"[^\"]*\"`. It will take all characters inside the `href` attribute till the nearest quote, just what we need.\n\nA working example:\n\n```js run\nlet str1 = '...<a href=\"link1\" class=\"wrong\">... <p style=\"\" class=\"doc\">...';\nlet str2 = '...<a href=\"link1\" class=\"doc\">... <a href=\"link2\" class=\"doc\">...';\nlet regexp = /<a href=\"[^\"]*\" class=\"doc\">/g;\n\n// Works!\nalert( str1.match(regexp) ); // null, no matches, that's correct\nalert( str2.match(regexp) ); // <a href=\"link1\" class=\"doc\">, <a href=\"link2\" class=\"doc\">\n```\n\n## Summary\n\nQuantifiers have two modes of work:\n\nGreedy\n: By default the regular expression engine tries to repeat the quantified character as many times as possible. For instance, `pattern:\\d+` consumes all possible digits. When it becomes impossible to consume more (no more digits or string end), then it continues to match the rest of the pattern. If there's no match then it decreases the number of repetitions (backtracks) and tries again.\n\nLazy\n: Enabled by the question mark `pattern:?` after the quantifier. The regexp engine tries to match the rest of the pattern before each repetition of the quantified character.\n\nAs we've seen, the lazy mode is not a \"panacea\" from the greedy search. An alternative is a \"fine-tuned\" greedy search, with exclusions, as in the pattern `pattern:\"[^\"]+\"`.\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/01-test-mac/solution.md",
    "content": "A two-digit hex number is `pattern:[0-9a-f]{2}` (assuming the flag `pattern:i` is set).\n\nWe need that number `NN`, and then `:NN` repeated 5 times (more numbers);\n\nThe regexp is: `pattern:[0-9a-f]{2}(:[0-9a-f]{2}){5}`\n\nNow let's show that the match should capture all the text: start at the beginning and end at the end. That's done by wrapping the pattern in `pattern:^...$`.\n\nFinally:\n\n```js run\nlet regexp = /^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$/i;\n\nalert( regexp.test('01:32:54:67:89:AB') ); // true\n\nalert( regexp.test('0132546789AB') ); // false (no colons)\n\nalert( regexp.test('01:32:54:67:89') ); // false (5 numbers, need 6)\n\nalert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ in the end)\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/01-test-mac/task.md",
    "content": "# Check MAC-address\n\n[MAC-address](https://en.wikipedia.org/wiki/MAC_address) of a network interface consists of 6 two-digit hex numbers separated by a colon.\n\nFor instance: `subject:'01:32:54:67:89:AB'`.\n\nWrite a regexp that checks whether a string is MAC-address.\n\nUsage:\n```js\nlet regexp = /your regexp/;\n\nalert( regexp.test('01:32:54:67:89:AB') ); // true\n\nalert( regexp.test('0132546789AB') ); // false (no colons)\n\nalert( regexp.test('01:32:54:67:89') ); // false (5 numbers, must be 6)\n\nalert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ at the end)\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md",
    "content": "A regexp to search 3-digit color `#abc`: `pattern:/#[a-f0-9]{3}/i`.\n\nWe can add exactly 3 more optional hex digits. We don't need more or less. The color has either 3 or 6 digits.\n\nLet's use the quantifier `pattern:{1,2}` for that: we'll have `pattern:/#([a-f0-9]{3}){1,2}/i`.\n\nHere the pattern `pattern:[a-f0-9]{3}` is enclosed in parentheses to apply the quantifier `pattern:{1,2}`.\n\nIn action:\n\n```js run\nlet regexp = /#([a-f0-9]{3}){1,2}/gi;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef #abc\n```\n\nThere's a minor problem here: the pattern found `match:#abc` in `subject:#abcd`. To prevent that we can add `pattern:\\b` to the end:\n\n```js run\nlet regexp = /#([a-f0-9]{3}){1,2}\\b/gi;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md",
    "content": "# Find color in the format #abc or #abcdef\n\nWrite a RegExp that matches colors in the format `#abc` or `#abcdef`. That is: `#` followed by 3 or 6 hexadecimal digits.\n\nUsage example:\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"color: #3f3; background-color: #AA00ef; and: #abcd\";\n\nalert( str.match(regexp) ); // #3f3 #AA00ef\n```\n\nP.S. This should be exactly 3 or 6 hex digits. Values with 4 digits, such as `#abcd`, should not match.\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md",
    "content": "A positive number with an optional decimal part is: `pattern:\\d+(\\.\\d+)?`.\n\nLet's add the optional `pattern:-` in the beginning:\n\n```js run\nlet regexp = /-?\\d+(\\.\\d+)?/g;\n\nlet str = \"-1.5 0 2 -123.4.\";\n\nalert( str.match(regexp) );   // -1.5, 0, 2, -123.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md",
    "content": "# Find all numbers\n\nWrite a regexp that looks for all decimal numbers including integer ones, with the floating point and negative ones.\n\nAn example of use:\n\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"-1.5 0 2 -123.4.\";\n\nalert( str.match(regexp) ); // -1.5, 0, 2, -123.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md",
    "content": "A regexp for a number is: `pattern:-?\\d+(\\.\\d+)?`. We created it in the previous task.\n\nAn operator is `pattern:[-+*/]`. The hyphen `pattern:-` goes first in the square brackets, because in the middle it would mean a character range, while we just want a character `-`.\n\nThe slash `/` should be escaped inside a JavaScript regexp `pattern:/.../`, we'll do that later.\n\nWe need a number, an operator, and then another number. And optional spaces between them.\n\nThe full regular expression: `pattern:-?\\d+(\\.\\d+)?\\s*[-+*/]\\s*-?\\d+(\\.\\d+)?`.\n\nIt has 3 parts, with `pattern:\\s*` between them:\n1. `pattern:-?\\d+(\\.\\d+)?` - the first number,\n1. `pattern:[-+*/]` - the operator,\n1. `pattern:-?\\d+(\\.\\d+)?` - the second number.\n\nTo make each of these parts a separate element of the result array, let's enclose them in parentheses: `pattern:(-?\\d+(\\.\\d+)?)\\s*([-+*/])\\s*(-?\\d+(\\.\\d+)?)`.\n\nIn action:\n\n```js run\nlet regexp = /(-?\\d+(\\.\\d+)?)\\s*([-+*\\/])\\s*(-?\\d+(\\.\\d+)?)/;\n\nalert( \"1.2 + 12\".match(regexp) );\n```\n\nThe result includes:\n\n- `result[0] == \"1.2 + 12\"` (full match)\n- `result[1] == \"1.2\"` (first group `(-?\\d+(\\.\\d+)?)` -- the first number, including the decimal part)\n- `result[2] == \".2\"` (second group`(\\.\\d+)?` -- the first decimal part)\n- `result[3] == \"+\"` (third group `([-+*\\/])` -- the operator)\n- `result[4] == \"12\"` (forth group `(-?\\d+(\\.\\d+)?)` -- the second number)\n- `result[5] == undefined` (fifth group `(\\.\\d+)?` -- the last decimal part is absent, so it's undefined)\n\nWe only want the numbers and the operator, without the full match or the decimal parts, so let's \"clean\" the result a bit.\n\nThe full match (the arrays first item) can be removed by shifting the array `result.shift()`.\n\nGroups that contain decimal parts (number 2 and 4) `pattern:(.\\d+)` can be excluded by adding  `pattern:?:` to the beginning: `pattern:(?:\\.\\d+)?`.\n\nThe final solution:\n\n```js run\nfunction parse(expr) {\n  let regexp = /(-?\\d+(?:\\.\\d+)?)\\s*([-+*\\/])\\s*(-?\\d+(?:\\.\\d+)?)/;\n\n  let result = expr.match(regexp);\n\n  if (!result) return [];\n  result.shift();\n\n  return result;\n}\n\nalert( parse(\"-1.23 * 3.45\") );  // -1.23, *, 3.45\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/04-parse-expression/task.md",
    "content": "# Parse an expression\n\nAn arithmetical expression consists of 2 numbers and an operator between them, for instance:\n\n- `1 + 2`\n- `1.2 * 3.4`\n- `-3 / -6`\n- `-2 - 2`\n\nThe operator is one of: `\"+\"`, `\"-\"`, `\"*\"` or `\"/\"`.\n\nThere may be extra spaces at the beginning, at the end or between the parts.\n\nCreate a function `parse(expr)` that takes an expression and returns an array of 3 items:\n\n1. The first number.\n2. The operator.\n3. The second number.\n\nFor example:\n\n```js\nlet [a, op, b] = parse(\"1.2 * 3.4\");\n\nalert(a); // 1.2\nalert(op); // *\nalert(b); // 3.4\n```\n"
  },
  {
    "path": "9-regular-expressions/11-regexp-groups/article.md",
    "content": "# Capturing groups\n\nA part of a pattern can be enclosed in parentheses `pattern:(...)`. This is called a \"capturing group\".\n\nThat has two effects:\n\n1. It allows to get a part of the match as a separate item in the result array.\n2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole.\n\n## Examples\n\nLet's see how parentheses work in examples.\n\n### Example: gogogo\n\nWithout parentheses, the pattern `pattern:go+` means `subject:g` character, followed by `subject:o` repeated one or more times. For instance, `match:goooo` or `match:gooooooooo`.\n\nParentheses group characters together, so `pattern:(go)+` means `match:go`, `match:gogo`, `match:gogogo` and so on.\n\n```js run\nalert( 'Gogogo now!'.match(/(go)+/ig) ); // \"Gogogo\"\n```\n\n### Example: domain\n\nLet's make something more complex -- a regular expression to search for a website domain.\n\nFor example:\n\n```\nmail.com\nusers.mail.com\nsmith.users.mail.com\n```\n\nAs we can see, a domain consists of repeated words, a dot after each one except the last one.\n\nIn regular expressions that's `pattern:(\\w+\\.)+\\w+`:\n\n```js run\nlet regexp = /(\\w+\\.)+\\w+/g;\n\nalert( \"site.com my.site.com\".match(regexp) ); // site.com,my.site.com\n```\n\nThe search works, but the pattern can't match a domain with a hyphen, e.g. `my-site.com`, because the hyphen does not belong to class `pattern:\\w`.\n\nWe can fix it by replacing `pattern:\\w` with `pattern:[\\w-]` in every word except the last one: `pattern:([\\w-]+\\.)+\\w+`.\n\n### Example: email\n\nThe previous example can be extended. We can create a regular expression for emails based on it.\n\nThe email format is: `name@domain`. Any word can be the name, hyphens and dots are allowed. In regular expressions that's `pattern:[-.\\w]+`.\n\nThe pattern:\n\n```js run\nlet regexp = /[-.\\w]+@([\\w-]+\\.)+[\\w-]+/g;\n\nalert(\"my@mail.com @ his@site.com.uk\".match(regexp)); // my@mail.com, his@site.com.uk\n```\n\nThat regexp is not perfect, but mostly works and helps to fix accidental mistypes. The only truly reliable check for an email can only be done by sending a letter.\n\n## Parentheses contents in the match\n\nParentheses are numbered from left to right. The search engine memorizes the content matched by each of them and allows to get it in the result.\n\nThe method `str.match(regexp)`, if `regexp` has no flag `g`, looks for the first match and returns it as an array:\n\n1. At index `0`: the full match.\n2. At index `1`: the contents of the first parentheses.\n3. At index `2`: the contents of the second parentheses.\n4. ...and so on...\n\nFor instance, we'd like to find HTML tags `pattern:<.*?>`, and process them. It would be convenient to have tag content (what's inside the angles), in a separate variable.\n\nLet's wrap the inner content into parentheses, like this: `pattern:<(.*?)>`.\n\nNow we'll get both the tag as a whole `match:<h1>` and its contents `match:h1` in the resulting array:\n\n```js run\nlet str = '<h1>Hello, world!</h1>';\n\nlet tag = str.match(/<(.*?)>/);\n\nalert( tag[0] ); // <h1>\nalert( tag[1] ); // h1\n```\n\n### Nested groups\n\nParentheses can be nested. In this case the numbering also goes from left to right.\n\nFor instance, when searching a tag in `subject:<span class=\"my\">` we may be interested in:\n\n1. The tag content as a whole: `match:span class=\"my\"`.\n2. The tag name: `match:span`.\n3. The tag attributes: `match:class=\"my\"`.\n\nLet's add parentheses for them: `pattern:<(([a-z]+)\\s*([^>]*))>`.\n\nHere's how they are numbered (left to right, by the opening paren):\n\n![](regexp-nested-groups-pattern.svg)\n\nIn action:\n\n```js run\nlet str = '<span class=\"my\">';\n\nlet regexp = /<(([a-z]+)\\s*([^>]*))>/;\n\nlet result = str.match(regexp);\nalert(result[0]); // <span class=\"my\">\nalert(result[1]); // span class=\"my\"\nalert(result[2]); // span\nalert(result[3]); // class=\"my\"\n```\n\nThe zero index of `result` always holds the full match.\n\nThen groups, numbered from left to right by an opening paren. The first group is returned as `result[1]`. Here it encloses the whole tag content.\n\nThen in `result[2]` goes the group from the second opening paren `pattern:([a-z]+)` - tag name, then in `result[3]` the tag: `pattern:([^>]*)`.\n\nThe contents of every group in the string:\n\n![](regexp-nested-groups-matches.svg)\n\n### Optional groups\n\nEven if a group is optional and doesn't exist in the match (e.g. has the quantifier `pattern:(...)?`), the corresponding `result` array item is present and equals `undefined`.\n\nFor instance, let's consider the regexp `pattern:a(z)?(c)?`. It looks for `\"a\"` optionally followed by `\"z\"` optionally followed by `\"c\"`.\n\nIf we run it on the string with a single letter `subject:a`, then the result is:\n\n```js run\nlet match = 'a'.match(/a(z)?(c)?/);\n\nalert( match.length ); // 3\nalert( match[0] ); // a (whole match)\nalert( match[1] ); // undefined\nalert( match[2] ); // undefined\n```\n\nThe array has the length of `3`, but all groups are empty.\n\nAnd here's a more complex match for the string `subject:ac`:\n\n```js run\nlet match = 'ac'.match(/a(z)?(c)?/)\n\nalert( match.length ); // 3\nalert( match[0] ); // ac (whole match)\nalert( match[1] ); // undefined, because there's nothing for (z)?\nalert( match[2] ); // c\n```\n\nThe array length is permanent: `3`. But there's nothing for the group `pattern:(z)?`, so the result is `[\"ac\", undefined, \"c\"]`.\n\n## Searching for all matches with groups: matchAll\n\n```warn header=\"`matchAll` is a new method, polyfill may be needed\"\nThe method `matchAll` is not supported in old browsers.\n\nA polyfill may be required, such as <https://github.com/ljharb/String.prototype.matchAll>.\n```\n\nWhen we search for all matches (flag `pattern:g`), the `match` method does not return contents for groups.\n\nFor example, let's find all tags in a string:\n\n```js run\nlet str = '<h1> <h2>';\n\nlet tags = str.match(/<(.*?)>/g);\n\nalert( tags ); // <h1>,<h2>\n```\n\nThe result is an array of matches, but without details about each of them. But in practice we usually need contents of capturing groups in the result.\n\nTo get them, we should search using the method `str.matchAll(regexp)`.\n\nIt was added to JavaScript language long after `match`, as its \"new and improved version\".\n\nJust like `match`, it looks for matches, but there are 3 differences:\n\n1. It returns not an array, but an iterable object.\n2. When the flag `pattern:g` is present, it returns every match as an array with groups.\n3. If there are no matches, it returns not `null`, but an empty iterable object.\n\nFor instance:\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\n// results - is not an array, but an iterable object\nalert(results); // [object RegExp String Iterator]\n\nalert(results[0]); // undefined (*)\n\nresults = Array.from(results); // let's turn it into array\n\nalert(results[0]); // <h1>,h1 (1st tag)\nalert(results[1]); // <h2>,h2 (2nd tag)\n```\n\nAs we can see, the first difference is very important, as demonstrated in the line `(*)`. We can't get the match as `results[0]`, because that object isn't pseudoarray. We can turn it into a real `Array` using `Array.from`. There are more details about pseudoarrays and iterables in the article <info:iterable>.\n\nThere's no need in `Array.from` if we're looping over results:\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\nfor(let result of results) {\n  alert(result);\n  // first alert: <h1>,h1\n  // second: <h2>,h2\n}\n```\n\n...Or using destructuring:\n\n```js\nlet [tag1, tag2] = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n```\n\nEvery match, returned by `matchAll`, has the same format as returned by `match` without flag `pattern:g`: it's an array with additional properties `index` (match index in the string) and `input` (source string):\n\n```js run\nlet results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);\n\nlet [tag1, tag2] = results;\n\nalert( tag1[0] ); // <h1>\nalert( tag1[1] ); // h1\nalert( tag1.index ); // 0\nalert( tag1.input ); // <h1> <h2>\n```\n\n```smart header=\"Why is a result of `matchAll` an iterable object, not an array?\"\nWhy is the method designed like that? The reason is simple - for the optimization.\n\nThe call to `matchAll` does not perform the search. Instead, it returns an iterable object, without the results initially. The search is performed each time we iterate over it, e.g. in the loop.\n\nSo, there will be found as many results as needed, not more.\n\nE.g. there are potentially 100 matches in the text, but in a `for..of` loop we found 5 of them, then decided it's enough and made a `break`. Then the engine won't spend time finding other 95 matches.\n```\n\n## Named groups\n\nRemembering groups by their numbers is hard. For simple patterns it's doable, but for more complex ones counting parentheses is inconvenient. We have a much better option: give names to parentheses.\n\nThat's done by putting `pattern:?<name>` immediately after the opening paren.\n\nFor example, let's look for a date in the format \"year-month-day\":\n\n```js run\n*!*\nlet dateRegexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/;\n*/!*\nlet str = \"2019-04-30\";\n\nlet groups = str.match(dateRegexp).groups;\n\nalert(groups.year); // 2019\nalert(groups.month); // 04\nalert(groups.day); // 30\n```\n\nAs you can see, the groups reside in the `.groups` property of the match.\n\nTo look for all dates, we can add flag `pattern:g`.\n\nWe'll also need `matchAll` to obtain full matches, together with groups:\n\n```js run\nlet dateRegexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g;\n\nlet str = \"2019-10-30 2020-01-01\";\n\nlet results = str.matchAll(dateRegexp);\n\nfor(let result of results) {\n  let {year, month, day} = result.groups;\n\n  alert(`${day}.${month}.${year}`);\n  // first alert: 30.10.2019\n  // second: 01.01.2020\n}\n```\n\n## Capturing groups in replacement\n\nMethod `str.replace(regexp, replacement)` that replaces all matches with `regexp` in `str` allows to use parentheses contents in the `replacement` string. That's done using `pattern:$n`, where `pattern:n` is the group number.\n\nFor example,\n\n```js run\nlet str = \"John Bull\";\nlet regexp = /(\\w+) (\\w+)/;\n\nalert( str.replace(regexp, '$2, $1') ); // Bull, John\n```\n\nFor named parentheses the reference will be `pattern:$<name>`.\n\nFor example, let's reformat dates from \"year-month-day\" to \"day.month.year\":\n\n```js run\nlet regexp = /(?<year>[0-9]{4})-(?<month>[0-9]{2})-(?<day>[0-9]{2})/g;\n\nlet str = \"2019-10-30, 2020-01-01\";\n\nalert( str.replace(regexp, '$<day>.$<month>.$<year>') );\n// 30.10.2019, 01.01.2020\n```\n\n## Non-capturing groups with ?:\n\nSometimes we need parentheses to correctly apply a quantifier, but we don't want their contents in results.\n\nA group may be excluded by adding `pattern:?:` in the beginning.\n\nFor instance, if we want to find `pattern:(go)+`, but don't want the parentheses contents (`go`) as a separate array item, we can write: `pattern:(?:go)+`.\n\nIn the example below we only get the name `match:John` as a separate member of the match:\n\n```js run\nlet str = \"Gogogo John!\";\n\n*!*\n// ?: excludes 'go' from capturing\nlet regexp = /(?:go)+ (\\w+)/i;\n*/!*\n\nlet result = str.match(regexp);\n\nalert( result[0] ); // Gogogo John (full match)\nalert( result[1] ); // John\nalert( result.length ); // 2 (no more items in the array)\n```\n\n## Summary\n\nParentheses group together a part of the regular expression, so that the quantifier applies to it as a whole.\n\nParentheses groups are numbered left-to-right, and can optionally be named with  `(?<name>...)`.\n\nThe content, matched by a group, can be obtained in the results:\n\n- The method `str.match` returns capturing groups only without flag `pattern:g`.\n- The method `str.matchAll` always returns capturing groups.\n\nIf the parentheses have no name, then their contents is available in the match array by its number. Named parentheses are also available in the property `groups`.\n\nWe can also use parentheses contents in the replacement string in `str.replace`: by the number `$n` or the name `$<name>`.\n\nA group may be excluded from numbering by adding `pattern:?:` in its start. That's used when we need to apply a quantifier to the whole group, but don't want it as a separate item in the results array. We also can't reference such parentheses in the replacement string.\n"
  },
  {
    "path": "9-regular-expressions/12-regexp-backreferences/article.md",
    "content": "# Backreferences in pattern: \\N and \\k<name>\n\nWe can use the contents of capturing groups `pattern:(...)` not only in the result or in the replacement string, but also in the pattern itself.\n\n## Backreference by number: \\N\n\nA group can be referenced in the pattern using `pattern:\\N`, where `N` is the group number.\n\nTo make clear why that's helpful, let's consider a task.\n\nWe need to find quoted strings: either single-quoted `subject:'...'` or a double-quoted `subject:\"...\"` -- both variants should match.\n\nHow to find them?\n\nWe can put both kinds of quotes in the square brackets: `pattern:['\"](.*?)['\"]`, but it would find strings with mixed quotes, like `match:\"...'` and `match:'...\"`. That would lead to incorrect matches when one quote appears inside other ones, like in the string `subject:\"She's the one!\"`:\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\nlet regexp = /['\"](.*?)['\"]/g;\n\n// The result is not what we'd like to have\nalert( str.match(regexp) ); // \"She'\n```\n\nAs we can see, the pattern found an opening quote `match:\"`, then the text is consumed till the other quote `match:'`, that closes the match.\n\nTo make sure that the pattern looks for the closing quote exactly the same as the opening one, we can wrap it into a capturing group and backreference it: `pattern:(['\"])(.*?)\\1`.\n\nHere's the correct code:\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\n*!*\nlet regexp = /(['\"])(.*?)\\1/g;\n*/!*\n\nalert( str.match(regexp) ); // \"She's the one!\"\n```\n\nNow it works! The regular expression engine finds the first quote `pattern:(['\"])` and memorizes its content. That's the first capturing group.\n\nFurther in the pattern `pattern:\\1` means \"find the same text as in the first group\", exactly the same quote in our case.\n\nSimilar to that, `pattern:\\2` would mean the contents of the second group, `pattern:\\3` - the 3rd group, and so on.\n\n```smart\nIf we use `?:` in the group, then we can't reference it. Groups that are excluded from capturing `(?:...)` are not memorized by the engine.\n```\n\n```warn header=\"Don't mess up: in the pattern `pattern:\\1`, in the replacement: `pattern:$1`\"\nIn the replacement string we use a dollar sign: `pattern:$1`, while in the pattern - a backslash `pattern:\\1`.\n```\n\n## Backreference by name: `\\k<name>`\n\nIf a regexp has many parentheses, it's convenient to give them names.\n\nTo reference a named group we can use `pattern:\\k<name>`.\n\nIn the example below the group with quotes is named `pattern:?<quote>`, so the backreference is `pattern:\\k<quote>`:\n\n```js run\nlet str = `He said: \"She's the one!\".`;\n\n*!*\nlet regexp = /(?<quote>['\"])(.*?)\\k<quote>/g;\n*/!*\n\nalert( str.match(regexp) ); // \"She's the one!\"\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md",
    "content": "\nThe first idea can be to list the languages with `|` in-between.\n\nBut that doesn't work right:\n\n```js run\nlet regexp = /Java|JavaScript|PHP|C|C\\+\\+/g;\n\nlet str = \"Java, JavaScript, PHP, C, C++\";\n\nalert( str.match(regexp) ); // Java,Java,PHP,C,C\n```\n\nThe regular expression engine looks for alternations one-by-one. That is: first it checks if we have  `match:Java`, otherwise -- looks for `match:JavaScript` and so on.\n\nAs a result, `match:JavaScript` can never be found, just because `match:Java` is checked first.\n\nThe same with `match:C` and `match:C++`.\n\nThere are two solutions for that problem:\n\n1. Change the order to check the longer match first: `pattern:JavaScript|Java|C\\+\\+|C|PHP`.\n2. Merge variants with the same start: `pattern:Java(Script)?|C(\\+\\+)?|PHP`.\n\nIn action:\n\n```js run\nlet regexp = /Java(Script)?|C(\\+\\+)?|PHP/g;\n\nlet str = \"Java, JavaScript, PHP, C, C++\";\n\nalert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md",
    "content": "# Find programming languages\n\nThere are many programming languages, for instance Java, JavaScript, PHP, C, C++.\n\nCreate a regexp that finds them in the string `subject:Java JavaScript PHP C++ C`:\n\n```js\nlet regexp = /your regexp/g;\n\nalert(\"Java JavaScript PHP C++ C\".match(regexp)); // Java JavaScript PHP C++ C\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md",
    "content": "\nOpening tag is `pattern:\\[(b|url|quote)\\]`.\n\nThen to find everything till the closing tag -- let's use the pattern `pattern:.*?` with flag `pattern:s` to match any character including the newline and then add a backreference to the closing tag.\n\nThe full pattern: `pattern:\\[(b|url|quote)\\].*?\\[/\\1\\]`.\n\nIn action:\n\n```js run\nlet regexp = /\\[(b|url|quote)\\].*?\\[\\/\\1\\]/gs;\n\nlet str = `\n  [b]hello![/b]\n  [quote]\n    [url]http://google.com[/url]\n  [/quote]\n`;\n\nalert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote]\n```\n\nPlease note that besides escaping `pattern:[` and `pattern:]`, we had to escape a slash for the closing tag `pattern:[\\/\\1]`, because normally the slash closes the pattern.\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md",
    "content": "# Find bbtag pairs\n\nA \"bb-tag\" looks like `[tag]...[/tag]`, where `tag` is one of: `b`, `url` or `quote`.\n\nFor instance:\n```\n[b]text[/b]\n[url]http://google.com[/url]\n```\n\nBB-tags can be nested. But a tag can't be nested into itself, for instance:\n\n```\nNormal:\n[url] [b]http://google.com[/b] [/url]\n[quote] [b]text[/b] [/quote]\n\nCan't happen:\n[b][b]text[/b][/b]\n```\n\nTags can contain line breaks, that's normal:\n\n```\n[quote]\n  [b]text[/b]\n[/quote]\n```\n\nCreate a regexp to find all BB-tags with their contents.\n\nFor instance:\n\n```js\nlet regexp = /your regexp/flags;\n\nlet str = \"..[url]http://google.com[/url]..\";\nalert( str.match(regexp) ); // [url]http://google.com[/url]\n```\n\nIf tags are nested, then we need the outer tag (if we want we can continue the search in its content):\n\n```js\nlet regexp = /your regexp/flags;\n\nlet str = \"..[url][b]http://google.com[/b][/url]..\";\nalert( str.match(regexp) ); // [url][b]http://google.com[/b][/url]\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md",
    "content": "The solution: `pattern:/\"(\\\\.|[^\"\\\\])*\"/g`.\n\nStep by step:\n\n- First we look for an opening quote `pattern:\"`\n- Then if we have a backslash `pattern:\\\\` (we have to double it in the pattern because it is a special character), then any character is fine after it (a dot).\n- Otherwise we take any character except a quote (that would mean the end of the string) and a backslash (to prevent lonely backslashes, the backslash is only used with some other symbol after it): `pattern:[^\"\\\\]`\n- ...And so on till the closing quote.\n\nIn action:\n\n```js run\nlet regexp = /\"(\\\\.|[^\"\\\\])*\"/g;\nlet str = ' .. \"test me\" .. \"Say \\\\\"Hello\\\\\"!\" .. \"\\\\\\\\ \\\\\"\" .. ';\n\nalert( str.match(regexp) ); // \"test me\",\"Say \\\"Hello\\\"!\",\"\\\\ \\\"\"\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md",
    "content": "# Find quoted strings\n\nCreate a regexp to find strings in double quotes `subject:\"...\"`.\n\nThe strings should support escaping, the same way as JavaScript strings do. For instance, quotes can be inserted as `subject:\\\"` a newline as `subject:\\n`, and the slash itself as `subject:\\\\`.\n\n```js\nlet str = \"Just like \\\"here\\\".\";\n```\n\nPlease note, in particular, that an escaped quote `subject:\\\"` does not end a string.\n\nSo we should search from one quote to the other ignoring escaped quotes on the way.\n\nThat's the essential part of the task, otherwise it would be trivial.\n\nExamples of strings to match:\n```js\n.. *!*\"test me\"*/!* ..  \n.. *!*\"Say \\\"Hello\\\"!\"*/!* ... (escaped quotes inside)\n.. *!*\"\\\\\"*/!* ..  (double slash inside)\n.. *!*\"\\\\ \\\"\"*/!* ..  (double slash and an escaped quote inside)\n```\n\nIn JavaScript we need to double the slashes to pass them right into the string, like this:\n\n```js run\nlet str = ' .. \"test me\" .. \"Say \\\\\"Hello\\\\\"!\" .. \"\\\\\\\\ \\\\\"\" .. ';\n\n// the in-memory string\nalert(str); //  .. \"test me\" .. \"Say \\\"Hello\\\"!\" .. \"\\\\ \\\"\" ..\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md",
    "content": "\nThe pattern start is obvious: `pattern:<style`.\n\n...But then we can't simply write `pattern:<style.*?>`, because `match:<styler>` would match it.\n\nWe need either a space after `match:<style` and then optionally something else or the ending `match:>`.\n\nIn the regexp language: `pattern:<style(>|\\s.*?>)`.\n\nIn action:\n\n```js run\nlet regexp = /<style(>|\\s.*?>)/g;\n\nalert( '<style> <styler> <style test=\"...\">'.match(regexp) ); // <style>, <style test=\"...\">\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md",
    "content": "# Find the full tag\n\nWrite a regexp to find the tag `<style...>`. It should match the full tag: it may have no attributes  `<style>` or have several of them `<style type=\"...\" id=\"...\">`.\n\n...But the regexp should not match `<styler>`!\n\nFor instance:\n\n```js\nlet regexp = /your regexp/g;\n\nalert( '<style> <styler> <style test=\"...\">'.match(regexp) ); // <style>, <style test=\"...\">\n```\n"
  },
  {
    "path": "9-regular-expressions/13-regexp-alternation/article.md",
    "content": "# Alternation (OR) |\n\nAlternation is the term in regular expression that is actually a simple \"OR\".\n\nIn a regular expression it is denoted with a vertical line character `pattern:|`.\n\nFor instance, we need to find programming languages: HTML, PHP, Java or JavaScript.\n\nThe corresponding regexp: `pattern:html|php|java(script)?`.\n\nA usage example:\n\n```js run\nlet regexp = /html|php|css|java(script)?/gi;\n\nlet str = \"First HTML appeared, then CSS, then JavaScript\";\n\nalert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript'\n```\n\nWe already saw a similar thing -- square brackets. They allow to choose between multiple characters, for instance `pattern:gr[ae]y` matches `match:gray` or `match:grey`.\n\nSquare brackets allow only characters or character classes. Alternation allows any expressions. A regexp `pattern:A|B|C` means one of expressions `A`, `B` or `C`.\n\nFor instance:\n\n- `pattern:gr(a|e)y` means exactly the same as `pattern:gr[ae]y`.\n- `pattern:gra|ey` means `match:gra` or `match:ey`.\n\nTo apply alternation to a chosen part of the pattern, we can enclose it in parentheses:\n- `pattern:I love HTML|CSS` matches `match:I love HTML` or `match:CSS`.\n- `pattern:I love (HTML|CSS)` matches `match:I love HTML` or `match:I love CSS`.\n\n## Example: regexp for time\n\nIn previous articles there was a task to build a regexp for searching time in the form `hh:mm`, for instance `12:00`. But a simple `pattern:\\d\\d:\\d\\d` is too vague. It accepts `25:99` as the time (as 99 minutes match the pattern, but that time is invalid).\n\nHow can we make a better pattern?\n\nWe can use more careful matching. First, the hours:\n\n- If the first digit is `0` or `1`, then the next digit can be any: `pattern:[01]\\d`.\n- Otherwise, if the first digit is `2`, then the next must be `pattern:[0-3]`.\n- (no other first digit is allowed)\n\nWe can write both variants in a regexp using alternation: `pattern:[01]\\d|2[0-3]`.\n\nNext, minutes must be from `00` to `59`. In the regular expression language that can be written as `pattern:[0-5]\\d`: the first digit `0-5`, and then any digit.\n\nIf we glue hours and minutes together, we get the pattern: `pattern:[01]\\d|2[0-3]:[0-5]\\d`.\n\nWe're almost done, but there's a problem. The alternation `pattern:|` now happens to be between `pattern:[01]\\d` and `pattern:2[0-3]:[0-5]\\d`.\n\nThat is: minutes are added to the second alternation variant, here's a clear picture:\n\n```\n[01]\\d  |  2[0-3]:[0-5]\\d\n```\n\nThat pattern looks for `pattern:[01]\\d` or `pattern:2[0-3]:[0-5]\\d`.\n\nBut that's wrong, the alternation should only be used in the \"hours\" part of the regular expression, to allow `pattern:[01]\\d` OR `pattern:2[0-3]`. Let's correct that by enclosing \"hours\" into parentheses: `pattern:([01]\\d|2[0-3]):[0-5]\\d`.\n\nThe final solution:\n\n```js run\nlet regexp = /([01]\\d|2[0-3]):[0-5]\\d/g;\n\nalert(\"00:00 10:10 23:59 25:99 1:2\".match(regexp)); // 00:00,10:10,23:59\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md",
    "content": "\nThe regexp for an integer number is `pattern:\\d+`.\n\nWe can exclude negatives by prepending it with the negative lookbehind: `pattern:(?<!-)\\d+`.\n\nAlthough, if we try it now, we may notice one more \"extra\" result:\n\n```js run\nlet regexp = /(?<!-)\\d+/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nconsole.log( str.match(regexp) ); // 0, 12, 123, *!*8*/!*\n```\n\nAs you can see, it matches `match:8`, from `subject:-18`. To exclude it, we need to ensure that the regexp starts matching a number not from the middle of another (non-matching) number.\n\nWe can do it by specifying another negative lookbehind: `pattern:(?<!-)(?<!\\d)\\d+`. Now `pattern:(?<!\\d)` ensures that a match does not start after another digit, just what we need.\n\nWe can also join them into a single lookbehind here:\n\n```js run\nlet regexp = /(?<![-\\d])\\d+/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nalert( str.match(regexp) ); // 0, 12, 123\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md",
    "content": "# Find non-negative integers\n\nThere's a string of integer numbers.\n\nCreate a regexp that looks for only non-negative ones (zero is allowed).\n\nAn example of use:\n```js\nlet regexp = /your regexp/g;\n\nlet str = \"0 12 -5 123 -18\";\n\nalert( str.match(regexp) ); // 0, 12, 123\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md",
    "content": "In order to insert after the `<body>` tag, we must first find it. We can use the regular expression pattern `pattern:<body.*?>` for that.\n\nIn this task we don't need to modify the `<body>` tag. We only need to add the text after it.\n\nHere's how we can do it:\n\n```js run\nlet str = '...<body style=\"...\">...';\nstr = str.replace(/<body.*?>/, '$&<h1>Hello</h1>');\n\nalert(str); // ...<body style=\"...\"><h1>Hello</h1>...\n```\n\nIn the replacement string `$&` means the match itself, that is, the part of the source text that corresponds to `pattern:<body.*?>`. It gets replaced by itself plus `<h1>Hello</h1>`.\n\nAn alternative is to use lookbehind:\n\n```js run\nlet str = '...<body style=\"...\">...';\nstr = str.replace(/(?<=<body.*?>)/, `<h1>Hello</h1>`);\n\nalert(str); // ...<body style=\"...\"><h1>Hello</h1>...\n```\n\nAs you can see, there's only lookbehind part in this regexp.\n\nIt works like this:\n- At every position in the text.\n- Check if it's preceeded by `pattern:<body.*?>`.\n- If it's so then we have the match.\n\nThe tag `pattern:<body.*?>` won't be returned. The result of this regexp is literally an empty string, but it matches only at positions preceeded by `pattern:<body.*?>`.\n\nSo it replaces the \"empty line\", preceeded by `pattern:<body.*?>`, with `<h1>Hello</h1>`. That's the insertion after `<body>`.\n\nP.S. Regexp flags, such as `pattern:s` and `pattern:i` can also be useful: `pattern:/<body.*?>/si`. The `pattern:s` flag makes the dot `pattern:.` match a newline character, and `pattern:i` flag makes `pattern:<body>` also match `match:<BODY>` case-insensitively.\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md",
    "content": "# Insert After Head\n\nWe have a string with an HTML Document.\n\nWrite a regular expression that inserts `<h1>Hello</h1>` immediately after `<body>` tag. The tag may have attributes.\n\nFor instance:\n\n```js\nlet regexp = /your regular expression/;\n\nlet str = `\n<html>\n  <body style=\"height: 200px\">\n  ...\n  </body>\n</html>\n`;\n\nstr = str.replace(regexp, `<h1>Hello</h1>`);\n```\n\nAfter that the value of `str` should be:\n```html\n<html>\n  <body style=\"height: 200px\"><h1>Hello</h1>\n  ...\n  </body>\n</html>\n```\n"
  },
  {
    "path": "9-regular-expressions/14-regexp-lookahead-lookbehind/article.md",
    "content": "# Lookahead and lookbehind\n\nSometimes we need to find only those matches for a pattern that are followed or preceded by another pattern.\n\nThere's a special syntax for that, called \"lookahead\" and \"lookbehind\", together referred to as \"lookaround\".\n\nFor the start, let's find the price from the string like `subject:1 turkey costs 30€`. That is: a number, followed by `subject:€` sign.\n\n## Lookahead\n\nThe syntax is: `pattern:X(?=Y)`, it means \"look for `pattern:X`, but match only if followed by `pattern:Y`\". There may be any pattern instead of `pattern:X` and `pattern:Y`.\n\nFor an integer number followed by `subject:€`, the regexp will be `pattern:\\d+(?=€)`:\n\n```js run\nlet str = \"1 turkey costs 30€\";\n\nalert( str.match(/\\d+(?=€)/) ); // 30, the number 1 is ignored, as it's not followed by €\n```\n\nPlease note: the lookahead is merely a test, the contents of the parentheses `pattern:(?=...)` is not included in the result `match:30`.\n\nWhen we look for `pattern:X(?=Y)`, the regular expression engine finds `pattern:X` and then checks if there's `pattern:Y` immediately after it. If it's not so, then the potential match is skipped, and the search continues.\n\nMore complex tests are possible, e.g. `pattern:X(?=Y)(?=Z)` means:\n\n1. Find `pattern:X`.\n2. Check if `pattern:Y` is immediately after `pattern:X` (skip if isn't).\n3. Check if `pattern:Z` is also immediately after `pattern:X` (skip if isn't).\n4. If both tests passed, then the `pattern:X` is a match, otherwise continue searching.\n\nIn other words, such pattern means that we're looking for `pattern:X` followed by `pattern:Y` and `pattern:Z` at the same time.\n\nThat's only possible if patterns `pattern:Y` and `pattern:Z` aren't mutually exclusive.\n\nFor example, `pattern:\\d+(?=\\s)(?=.*30)` looks for `pattern:\\d+` that is followed by a space `pattern:(?=\\s)`, and there's `30` somewhere after it `pattern:(?=.*30)`:\n\n```js run\nlet str = \"1 turkey costs 30€\";\n\nalert( str.match(/\\d+(?=\\s)(?=.*30)/) ); // 1\n```\n\nIn our string that exactly matches the number `1`.\n\n## Negative lookahead\n\nLet's say that we want a quantity instead, not a price from the same string. That's a number `pattern:\\d+`, NOT followed by `subject:€`.\n\nFor that, a negative lookahead can be applied.\n\nThe syntax is: `pattern:X(?!Y)`, it means \"search `pattern:X`, but only if not followed by `pattern:Y`\".\n\n```js run\nlet str = \"2 turkeys cost 60€\";\n\nalert( str.match(/\\d+\\b(?!€)/g) ); // 2 (the price is not matched)\n```\n\n## Lookbehind\n\nLookahead allows to add a condition for \"what follows\".\n\nLookbehind is similar, but it looks behind. That is, it allows to match a pattern only if there's something before it.\n\nThe syntax is:\n- Positive lookbehind: `pattern:(?<=Y)X`, matches `pattern:X`, but only if there's  `pattern:Y` before it.\n- Negative lookbehind: `pattern:(?<!Y)X`, matches `pattern:X`, but only if there's no `pattern:Y` before it.\n\nFor example, let's change the price to US dollars. The dollar sign is usually before the number, so to look for `$30` we'll use `pattern:(?<=\\$)\\d+` -- an amount preceded by `subject:$`:\n\n```js run\nlet str = \"1 turkey costs $30\";\n\n// the dollar sign is escaped \\$\nalert( str.match(/(?<=\\$)\\d+/) ); // 30 (skipped the sole number)\n```\n\nAnd, if we need the quantity -- a number, not preceded by `subject:$`, then we can use a negative lookbehind `pattern:(?<!\\$)\\d+`:\n\n```js run\nlet str = \"2 turkeys cost $60\";\n\nalert( str.match(/(?<!\\$)\\b\\d+/g) ); // 2 (the price is not matched)\n```\n\n## Capturing groups\n\nGenerally, the contents inside lookaround parentheses does not become a part of the result.\n\nE.g. in the pattern `pattern:\\d+(?=€)`, the `pattern:€` sign doesn't get captured as a part of the match. That's natural: we look for a number `pattern:\\d+`, while `pattern:(?=€)` is just a test that it should be followed by `subject:€`.\n\nBut in some situations we might want to capture the lookaround expression as well, or a part of it. That's possible. Just wrap that part into additional parentheses.\n\nIn the example below the currency sign `pattern:(€|kr)` is captured, along with the amount:\n\n```js run\nlet str = \"1 turkey costs 30€\";\nlet regexp = /\\d+(?=(€|kr))/; // extra parentheses around €|kr\n\nalert( str.match(regexp) ); // 30, €\n```\n\nAnd here's the same for lookbehind:\n\n```js run\nlet str = \"1 turkey costs $30\";\nlet regexp = /(?<=(\\$|£))\\d+/;\n\nalert( str.match(regexp) ); // 30, $\n```\n\n## Summary\n\nLookahead and lookbehind (commonly referred to as \"lookaround\") are useful when we'd like to match something depending on the context before/after it.\n\nFor simple regexps we can do the similar thing manually. That is: match everything, in any context, and then filter by context in the loop.\n\nRemember, `str.match` (without flag `pattern:g`) and `str.matchAll` (always) return matches as arrays with `index` property, so we know where exactly in the text it is, and can check the context.\n\nBut generally lookaround is more convenient.\n\nLookaround types:\n\n| Pattern            | type             | matches |\n|--------------------|------------------|---------|\n| `X(?=Y)`   | Positive lookahead | `pattern:X` if followed by `pattern:Y` |\n| `X(?!Y)`   | Negative lookahead | `pattern:X` if not followed by `pattern:Y` |\n| `(?<=Y)X` |  Positive lookbehind | `pattern:X` if after `pattern:Y` |\n| `(?<!Y)X` | Negative lookbehind | `pattern:X` if not after `pattern:Y` |\n"
  },
  {
    "path": "9-regular-expressions/15-regexp-catastrophic-backtracking/article.md",
    "content": "# Catastrophic backtracking\n\nSome regular expressions are looking simple, but can execute a veeeeeery long time, and even \"hang\" the JavaScript engine.\n\nSooner or later most developers occasionally face such behavior. The typical symptom -- a regular expression works fine sometimes, but for certain strings it \"hangs\", consuming 100% of CPU.\n\nIn such case a web-browser suggests to kill the script and reload the page. Not a good thing for sure.\n\nFor server-side JavaScript such a regexp may hang the server process, that's even worse. So we definitely should take a look at it.\n\n## Example\n\nLet's say we have a string, and we'd like to check if it consists of words `pattern:\\w+` with an optional space `pattern:\\s?` after each.\n\nAn obvious way to construct a regexp would be to take a word followed by an optional space `pattern:\\w+\\s?` and then repeat it with `*`.\n\nThat leads us to the regexp `pattern:^(\\w+\\s?)*$`, it specifies zero or more such words, that start at the beginning `pattern:^` and finish at the end `pattern:$` of the line.\n\nIn action:\n\n```js run\nlet regexp = /^(\\w+\\s?)*$/;\n\nalert( regexp.test(\"A good string\") ); // true\nalert( regexp.test(\"Bad characters: $@#\") ); // false\n```\n\nThe regexp seems to work. The result is correct. Although, on certain strings it takes a lot of time. So long that JavaScript engine \"hangs\" with 100% CPU consumption.\n\nIf you run the example below, you probably won't see anything, as JavaScript will just \"hang\". A web-browser will stop reacting on events, the UI will stop working (most browsers allow only scrolling). After some time it will suggest to reload the page. So be careful with this:\n\n```js run\nlet regexp = /^(\\w+\\s?)*$/;\nlet str = \"An input string that takes a long time or even makes this regexp hang!\";\n\n// will take a very long time\nalert( regexp.test(str) );\n```\n\nTo be fair, let's note that some regular expression engines can handle such a search effectively, for example V8 engine version starting from 8.8 can do that (so Google Chrome 88 doesn't hang here), while Firefox browser does hang. \n\n## Simplified example\n\nWhat's the matter? Why does the regular expression hang?\n\nTo understand that, let's simplify the example: remove spaces `pattern:\\s?`. Then it becomes `pattern:^(\\w+)*$`.\n\nAnd, to make things more obvious, let's replace `pattern:\\w` with `pattern:\\d`. The resulting regular expression still hangs, for instance:\n\n```js run\nlet regexp = /^(\\d+)*$/;\n\nlet str = \"012345678901234567890123456789z\";\n\n// will take a very long time (careful!)\nalert( regexp.test(str) );\n```\n\nSo what's wrong with the regexp?\n\nFirst, one may notice that the regexp `pattern:(\\d+)*` is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\\d+`.\n\nIndeed, the regexp is artificial; we got it by simplifying the previous example. But the reason why it is slow is the same. So let's understand it, and then the previous example will become obvious.\n\nWhat happens during the search of `pattern:^(\\d+)*$` in the line `subject:123456789z` (shortened a bit for clarity, please note a non-digit character `subject:z` at the end, it's important), why does it take so long?\n\nHere's what the regexp engine does:\n\n1. First, the regexp engine tries to find the content of the parentheses: the number `pattern:\\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits:\n\n    ```\n    \\d+.......\n    (123456789)z\n    ```\n\n    After all digits are consumed, `pattern:\\d+` is considered found (as `match:123456789`).\n\n    Then the star quantifier `pattern:(\\d+)*` applies. But there are no more digits in the text, so the star doesn't give anything.\n\n    The next character in the pattern is the string end `pattern:$`. But in the text we have `subject:z` instead, so there's no match:\n\n    ```\n               X\n    \\d+........$\n    (123456789)z\n    ```\n\n2. As there's no match, the greedy quantifier `pattern:+` decreases the count of repetitions, backtracks one character back.\n\n    Now `pattern:\\d+` takes all digits except the last one (`match:12345678`):\n    ```\n    \\d+.......\n    (12345678)9z\n    ```\n3. Then the engine tries to continue the search from the next position (right after `match:12345678`).\n\n    The star `pattern:(\\d+)*` can be applied -- it gives one more match of `pattern:\\d+`, the number `match:9`:\n\n    ```\n\n    \\d+.......\\d+\n    (12345678)(9)z\n    ```\n\n    The engine tries to match `pattern:$` again, but fails, because it meets `subject:z` instead:\n\n    ```\n                 X\n    \\d+.......\\d+\n    (12345678)(9)z\n    ```\n\n\n4. There's no match, so the engine will continue backtracking, decreasing the number of repetitions. Backtracking generally works like this: the last greedy quantifier decreases the number of repetitions until it reaches the minimum. Then the previous greedy quantifier decreases, and so on.\n\n    All possible combinations are attempted. Here are their examples.\n\n    The first number `pattern:\\d+` has 7 digits, and then a number of 2 digits:\n\n    ```\n                 X\n    \\d+......\\d+\n    (1234567)(89)z\n    ```\n\n    The first number has 7 digits, and then two numbers of 1 digit each:\n\n    ```\n                   X\n    \\d+......\\d+\\d+\n    (1234567)(8)(9)z\n    ```\n\n    The first number has 6 digits, and then a number of 3 digits:\n\n    ```\n                 X\n    \\d+.......\\d+\n    (123456)(789)z\n    ```\n\n    The first number has 6 digits, and then 2 numbers:\n\n    ```\n                   X\n    \\d+.....\\d+ \\d+\n    (123456)(78)(9)z\n    ```\n\n    ...And so on.\n\n\nThere are many ways to split a sequence of digits `123456789` into numbers. To be precise, there are <code>2<sup>n</sup>-1</code>, where `n` is the length of the sequence.\n\n- For `123456789` we have `n=9`, that gives 511 combinations.\n- For a longer sequence with `n=20` there are about one million (1048575) combinations.\n- For `n=30` - a thousand times more (1073741823 combinations).\n\nTrying each of them is exactly the reason why the search takes so long.\n\n## Back to words and strings\n\nThe similar thing happens in our first example, when we look for words by pattern `pattern:^(\\w+\\s?)*$` in the string `subject:An input that hangs!`.\n\nThe reason is that a word can be represented as one `pattern:\\w+` or many:\n\n```\n(input)\n(inpu)(t)\n(inp)(u)(t)\n(in)(p)(ut)\n...\n```\n\nFor a human, it's obvious that there may be no match, because the string ends with an exclamation sign `!`, but the regular expression expects a wordly character `pattern:\\w` or a space `pattern:\\s` at the end. But the engine doesn't know that.\n\nIt tries all combinations of how the regexp `pattern:(\\w+\\s?)*` can \"consume\" the string, including variants with spaces `pattern:(\\w+\\s)*` and without them `pattern:(\\w+)*` (because spaces `pattern:\\s?` are optional). As there are many such combinations (we've seen it with digits), the search takes a lot of time.\n\nWhat to do?\n\nShould we turn on the lazy mode?\n\nUnfortunately, that won't help: if we replace `pattern:\\w+` with `pattern:\\w+?`, the regexp will still hang. The order of combinations will change, but not their total count.\n\nSome regular expression engines have tricky tests and finite automations that allow to avoid going through all combinations or make it much faster, but most engines don't, and it doesn't always help.\n\n## How to fix?\n\nThere are two main approaches to fixing the problem.\n\nThe first is to lower the number of possible combinations.\n\nLet's make the space non-optional by rewriting the regular expression as `pattern:^(\\w+\\s)*\\w*$` - we'll look for any number of words followed by a space `pattern:(\\w+\\s)*`, and then (optionally) a final word `pattern:\\w*`.\n\nThis regexp is equivalent to the previous one (matches the same) and works well:\n\n```js run\nlet regexp = /^(\\w+\\s)*\\w*$/;\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false\n```\n\nWhy did the problem disappear?\n\nThat's because now the space is mandatory.\n\nThe previous regexp, if we omit the space, becomes `pattern:(\\w+)*`, leading to many combinations of `\\w+` within a single word\n\nSo `subject:input` could be matched as two repetitions of `pattern:\\w+`, like this:\n\n```\n\\w+  \\w+\n(inp)(ut)\n```\n\nThe new pattern is different: `pattern:(\\w+\\s)*` specifies repetitions of words followed by a space! The `subject:input` string can't be matched as two repetitions of `pattern:\\w+\\s`, because the space is mandatory.\n\nThe time needed to try a lot of (actually most of) combinations is now saved.\n\n## Preventing backtracking\n\nIt's not always convenient to rewrite a regexp though. In the example above it was easy, but it's not always obvious how to do it.\n\nBesides, a rewritten regexp is usually more complex, and that's not good. Regexps are complex enough without extra efforts.\n\nLuckily, there's an alternative approach. We can forbid backtracking for the quantifier.\n\nThe root of the problem is that the regexp engine tries many combinations that are obviously wrong for a human.\n\nE.g. in the regexp `pattern:(\\d+)*$` it's obvious for a human, that `pattern:+` shouldn't backtrack. If we replace one `pattern:\\d+` with two separate `pattern:\\d+\\d+`, nothing changes:\n\n```\n\\d+........\n(123456789)!\n\n\\d+...\\d+....\n(1234)(56789)!\n```\n\nAnd in the original example `pattern:^(\\w+\\s?)*$` we may want to forbid backtracking in `pattern:\\w+`. That is: `pattern:\\w+` should match a whole word, with the maximal possible length. There's no need to lower the repetitions count in `pattern:\\w+` or to split it into two words `pattern:\\w+\\w+` and so on.\n\nModern regular expression engines support possessive quantifiers for that. Regular quantifiers become possessive if we add `pattern:+` after them. That is, we use `pattern:\\d++` instead of `pattern:\\d+` to stop `pattern:+` from backtracking.\n\nPossessive quantifiers are in fact simpler than \"regular\" ones. They just match as many as they can, without any backtracking. The search process without backtracking is simpler.\n\nThere are also so-called \"atomic capturing groups\" - a way to disable backtracking inside parentheses.\n\n...But the bad news is that, unfortunately, in JavaScript they are not supported.\n\nWe can emulate them though using a \"lookahead transform\".\n\n### Lookahead to the rescue!\n\nSo we've come to real advanced topics. We'd like a quantifier, such as `pattern:+` not to backtrack, because sometimes backtracking makes no sense.\n\nThe pattern to take as many repetitions of `pattern:\\w` as possible without backtracking is: `pattern:(?=(\\w+))\\1`. Of course, we could take another pattern instead of `pattern:\\w`.\n\nThat may seem odd, but it's actually a very simple transform.\n\nLet's decipher it:\n\n- Lookahead `pattern:?=` looks forward for the longest word `pattern:\\w+` starting at the current position.\n- The contents of parentheses with `pattern:?=...` isn't memorized by the engine, so wrap `pattern:\\w+` into parentheses. Then the engine will memorize their contents\n- ...And allow us to reference it in the pattern as `pattern:\\1`.\n\nThat is: we look ahead - and if there's a word `pattern:\\w+`, then match it as `pattern:\\1`.\n\nWhy? That's because the lookahead finds a word `pattern:\\w+` as a whole and we capture it into the pattern with `pattern:\\1`. So we essentially implemented a possessive plus `pattern:+` quantifier. It captures only the whole word `pattern:\\w+`, not a part of it.\n\nFor instance, in the word `subject:JavaScript` it may not only match `match:Java`, but leave out `match:Script` to match the rest of the pattern.\n\nHere's the comparison of two patterns:\n\n```js run\nalert( \"JavaScript\".match(/\\w+Script/)); // JavaScript\nalert( \"JavaScript\".match(/(?=(\\w+))\\1Script/)); // null\n```\n\n1. In the first variant `pattern:\\w+` first captures the whole word `subject:JavaScript` but then `pattern:+` backtracks character by character, to try to match the rest of the pattern, until it finally succeeds (when `pattern:\\w+` matches `match:Java`).\n2. In the second variant `pattern:(?=(\\w+))` looks ahead and finds the word  `subject:JavaScript`, that is included into the pattern as a whole by `pattern:\\1`, so there remains no way to find `subject:Script` after it.\n\nWe can put a more complex regular expression into `pattern:(?=(\\w+))\\1` instead of `pattern:\\w`, when we need to forbid backtracking for `pattern:+` after it.\n\n```smart\nThere'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).\n```\n\nLet's rewrite the first example using lookahead to prevent backtracking:\n\n```js run\nlet regexp = /^((?=(\\w+))\\2\\s?)*$/;\n\nalert( regexp.test(\"A good string\") ); // true\n\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false, works and fast!\n```\n\nHere `pattern:\\2` is used instead of `pattern:\\1`, because there are additional outer parentheses. To avoid messing up with the numbers, we can give the parentheses a name, e.g. `pattern:(?<word>\\w+)`.\n\n```js run\n// parentheses are named ?<word>, referenced as \\k<word>\nlet regexp = /^((?=(?<word>\\w+))\\k<word>\\s?)*$/;\n\nlet str = \"An input string that takes a long time or even makes this regex hang!\";\n\nalert( regexp.test(str) ); // false\n\nalert( regexp.test(\"A correct string\") ); // true\n```\n\nThe problem described in this article is called \"catastrophic backtracking\".\n\nWe covered two ways how to solve it:\n- Rewrite the regexp to lower the possible combinations count.\n- Prevent backtracking.\n"
  },
  {
    "path": "9-regular-expressions/16-regexp-sticky/article.md",
    "content": "\n# Sticky flag \"y\", searching at position\n\nThe flag `pattern:y` allows to perform the search at the given position in the source string.\n\nTo grasp the use case of `pattern:y` flag, and better understand the ways of regexps, let's explore a practical example.\n\nOne of common tasks for regexps is \"lexical analysis\": we get a text, e.g. in a programming language, and need to find its structural elements. For instance, HTML has tags and attributes, JavaScript code has functions, variables, and so on.\n\nWriting lexical analyzers is a special area, with its own tools and algorithms, so we don't go deep in there, but there's a common task: to read something at the given position.\n\nE.g. we have a code string `subject:let varName = \"value\"`, and we need to read the variable name from it, that starts at position `4`.\n\nWe'll look for variable name using regexp `pattern:\\w+`. Actually, JavaScript variable names need a bit more complex regexp for accurate matching, but here it doesn't matter.\n\n- A call to `str.match(/\\w+/)` will find only the first word in the line (`let`). That's not it.\n- We can add the flag `pattern:g`. But then the call `str.match(/\\w+/g)` will look for all words in the text, while we need one word at position `4`. Again, not what we need.\n\n**So, how to search for a regexp exactly at the given position?**\n\nLet's try using method `regexp.exec(str)`.\n\nFor a `regexp` without flags `pattern:g` and `pattern:y`, this method looks only for the first match, it works exactly like `str.match(regexp)`.\n\n...But if there's flag `pattern:g`, then it performs the search in `str`, starting from position stored in the `regexp.lastIndex` property. And, if it finds a match, then sets `regexp.lastIndex` to the index immediately after the match.\n\nIn other words, `regexp.lastIndex` serves as a starting point for the search, that each `regexp.exec(str)` call resets to the new value (\"after the last match\"). That's only if there's `pattern:g` flag, of course.\n\nSo, successive calls to `regexp.exec(str)` return matches one after another.\n\nHere's an example of such calls:\n\n```js run\nlet str = 'let varName'; // Let's find all words in this string\nlet regexp = /\\w+/g;\n\nalert(regexp.lastIndex); // 0 (initially lastIndex=0)\n\nlet word1 = regexp.exec(str);\nalert(word1[0]); // let (1st word)\nalert(regexp.lastIndex); // 3 (position after the match)\n\nlet word2 = regexp.exec(str);\nalert(word2[0]); // varName (2nd word)\nalert(regexp.lastIndex); // 11 (position after the match)\n\nlet word3 = regexp.exec(str);\nalert(word3); // null (no more matches)\nalert(regexp.lastIndex); // 0 (resets at search end)\n```\n\nWe can get all matches in the loop:\n\n```js run\nlet str = 'let varName';\nlet regexp = /\\w+/g;\n\nlet result;\n\nwhile (result = regexp.exec(str)) {\n  alert( `Found ${result[0]} at position ${result.index}` );\n  // Found let at position 0, then\n  // Found varName at position 4\n}\n```\n\nSuch use of `regexp.exec` is an alternative to method `str.matchAll`, with a bit more control over the process.\n\nLet's go back to our task.\n\nWe can manually set `lastIndex` to `4`, to start the search from the given position!\n\nLike this:\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/g; // without flag \"g\", property lastIndex is ignored\n\n*!*\nregexp.lastIndex = 4;\n*/!*\n\nlet word = regexp.exec(str);\nalert(word); // varName\n```\n\nHooray! Problem solved! \n\nWe performed a search of `pattern:\\w+`, starting from position `regexp.lastIndex = 4`.\n\nThe result is correct.\n\n...But wait, not so fast.\n\nPlease note: the `regexp.exec` call starts searching at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found:\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/g;\n\n*!*\n// start the search from position 3\nregexp.lastIndex = 3;\n*/!*\n\nlet word = regexp.exec(str); \n// found the match at position 4\nalert(word[0]); // varName\nalert(word.index); // 4\n```\n\nFor some tasks, including the lexical analysis, that's just wrong. We need to find a match exactly at the given position at the text, not somewhere after it. And that's what the flag `y` is for.\n\n**The flag `pattern:y` makes `regexp.exec` to search exactly at position `lastIndex`, not \"starting from\" it.**\n\nHere's the same search with flag `pattern:y`:\n\n```js run\nlet str = 'let varName = \"value\"';\n\nlet regexp = /\\w+/y;\n\nregexp.lastIndex = 3;\nalert( regexp.exec(str) ); // null (there's a space at position 3, not a word)\n\nregexp.lastIndex = 4;\nalert( regexp.exec(str) ); // varName (word at position 4)\n```\n\nAs we can see, regexp `pattern:/\\w+/y` doesn't match at position `3` (unlike the flag  `pattern:g`), but matches at position `4`.\n\nNot only that's what we need, there's an important performance gain when using flag `pattern:y`.\n\nImagine, we have a long text, and there are no matches in it, at all. Then a search with flag `pattern:g` will go till the end of the text and find nothing, and this will take significantly more time than the search with flag `pattern:y`, that checks only the exact position.\n\nIn tasks like lexical analysis, there are usually many searches at an exact position, to check what we have there. Using flag `pattern:y` is the key for correct implementations and a good performance.\n"
  },
  {
    "path": "9-regular-expressions/17-regexp-methods/article.md",
    "content": "# Methods of RegExp and String\n\nIn this article we'll cover various methods that work with regexps in-depth.\n\n## str.match(regexp)\n\nThe method `str.match(regexp)` finds matches for `regexp` in the string `str`.\n\nIt has 3 modes:\n\n1. If the `regexp` doesn't have flag `pattern:g`, then it returns the first match as an array with capturing groups and properties `index` (position of the match), `input` (input string, equals `str`):\n\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/Java(Script)/);\n\n    alert( result[0] );     // JavaScript (full match)\n    alert( result[1] );     // Script (first capturing group)\n    alert( result.length ); // 2\n\n    // Additional information:\n    alert( result.index );  // 7 (match position)\n    alert( result.input );  // I love JavaScript (source string)\n    ```\n\n2. If the `regexp` has flag `pattern:g`, then it returns an array of all matches as strings, without capturing groups and other details.\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/Java(Script)/g);\n\n    alert( result[0] ); // JavaScript\n    alert( result.length ); // 1\n    ```\n\n3. If there are no matches, no matter if there's flag `pattern:g` or not, `null` is returned.\n\n    That's an important nuance. If there are no matches, we don't get an empty array, but `null`. It's easy to make a mistake forgetting about it, e.g.:\n\n    ```js run\n    let str = \"I love JavaScript\";\n\n    let result = str.match(/HTML/);\n\n    alert(result); // null\n    alert(result.length); // Error: Cannot read property 'length' of null\n    ```\n\n    If we want the result to be an array, we can write like this:\n\n    ```js\n    let result = str.match(regexp) || [];\n    ```\n\n## str.matchAll(regexp)\n\n[recent browser=\"new\"]\n\nThe method `str.matchAll(regexp)` is a \"newer, improved\" variant of `str.match`.\n\nIt's used mainly to search for all matches with all groups.\n\nThere are 3 differences from `match`:\n\n1. It returns an iterable object with matches instead of an array. We can make a regular array from it using `Array.from`.\n2. Every match is returned as an array with capturing groups (the same format as `str.match` without flag `pattern:g`).\n3. If there are no results, it returns an empty iterable object instead of `null`.\n\nUsage example:\n\n```js run\nlet str = '<h1>Hello, world!</h1>';\nlet regexp = /<(.*?)>/g;\n\nlet matchAll = str.matchAll(regexp);\n\nalert(matchAll); // [object RegExp String Iterator], not array, but an iterable\n\nmatchAll = Array.from(matchAll); // array now\n\nlet firstMatch = matchAll[0];\nalert( firstMatch[0] );  // <h1>\nalert( firstMatch[1] );  // h1\nalert( firstMatch.index );  // 0\nalert( firstMatch.input );  // <h1>Hello, world!</h1>\n```\n\nIf we use `for..of` to loop over `matchAll` matches, then we don't need `Array.from` any more.\n\n## str.split(regexp|substr, limit)\n\nSplits the string using the regexp (or a substring) as a delimiter.\n\nWe can use `split` with strings, like this:\n\n```js run\nalert('12-34-56'.split('-')) // array of ['12', '34', '56']\n```\n\nBut we can split by a regular expression, the same way:\n\n```js run\nalert('12, 34, 56'.split(/,\\s*/)) // array of ['12', '34', '56']\n```\n\n## str.search(regexp)\n\nThe method `str.search(regexp)` returns the position of the first match or `-1` if none found:\n\n```js run\nlet str = \"A drop of ink may make a million think\";\n\nalert( str.search( /ink/i ) ); // 10 (first match position)\n```\n\n**The important limitation: `search` only finds the first match.**\n\nIf we need positions of further matches, we should use other means, such as finding them all with `str.matchAll(regexp)`.\n\n## str.replace(str|regexp, str|func)\n\nThis is a generic method for searching and replacing, one of most useful ones. The swiss army knife for searching and replacing.  \n\nWe can use it without regexps, to search and replace a substring:\n\n```js run\n// replace a dash by a colon\nalert('12-34-56'.replace(\"-\", \":\")) // 12:34-56\n```\n\nThere's a pitfall though.\n\n**When the first argument of `replace` is a string, it only replaces the first match.**\n\nYou can see that in the example above: only the first `\"-\"` is replaced by `\":\"`.\n\nTo find all hyphens, we need to use not the string `\"-\"`, but a regexp `pattern:/-/g`, with the obligatory `pattern:g` flag:\n\n```js run\n// replace all dashes by a colon\nalert( '12-34-56'.replace( *!*/-/g*/!*, \":\" ) )  // 12:34:56\n```\n\nThe second argument is a replacement string. We can use special characters in it:\n\n| Symbols | Action in the replacement string |\n|--------|--------|\n|`$&`|inserts the whole match|\n|<code>$&#096;</code>|inserts a part of the string before the match|\n|`$'`|inserts a part of the string after the match|\n|`$n`|if `n` is a 1-2 digit number, inserts the contents of n-th capturing group, for details see [](info:regexp-groups)|\n|`$<name>`|inserts the contents of the parentheses with the given `name`, for details see [](info:regexp-groups)|\n|`$$`|inserts character `$` |\n\nFor instance:\n\n```js run\nlet str = \"John Smith\";\n\n// swap first and last name\nalert(str.replace(/(john) (smith)/i, '$2, $1')) // Smith, John\n```\n\n**For situations that require \"smart\" replacements, the second argument can be a function.**\n\nIt will be called for each match, and the returned value will be inserted as a replacement.\n\nThe function is called with arguments `func(match, p1, p2, ..., pn, offset, input, groups)`:\n\n1. `match` -- the match,\n2. `p1, p2, ..., pn` -- contents of capturing groups (if there are any),\n3. `offset` -- position of the match,\n4. `input` -- the source string,\n5. `groups` -- an object with named groups.\n\nIf there are no parentheses in the regexp, then there are only 3 arguments: `func(str, offset, input)`.\n\nFor example, let's uppercase all matches:\n\n```js run\nlet str = \"html and css\";\n\nlet result = str.replace(/html|css/gi, str => str.toUpperCase());\n\nalert(result); // HTML and CSS\n```\n\nReplace each match by its position in the string:\n\n```js run\nalert(\"Ho-Ho-ho\".replace(/ho/gi, (match, offset) => offset)); // 0-3-6\n```\n\nIn the example below there are two parentheses, so the replacement function is called with 5 arguments: the first is the full match, then 2 parentheses, and after it (not used in the example) the match position and the source string:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(\\w+) (\\w+)/, (match, name, surname) => `${surname}, ${name}`);\n\nalert(result); // Smith, John\n```\n\nIf there are many groups, it's convenient to use rest parameters to access them:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(\\w+) (\\w+)/, (...match) => `${match[2]}, ${match[1]}`);\n\nalert(result); // Smith, John\n```\n\nOr, if we're using named groups, then `groups` object with them is always the last, so we can obtain it like this:\n\n```js run\nlet str = \"John Smith\";\n\nlet result = str.replace(/(?<name>\\w+) (?<surname>\\w+)/, (...match) => {\n  let groups = match.pop();\n\n  return `${groups.surname}, ${groups.name}`;\n});\n\nalert(result); // Smith, John\n```\n\nUsing a function gives us the ultimate replacement power, because it gets all the information about the match, has access to outer variables and can do everything.\n\n## str.replaceAll(str|regexp, str|func)\n\nThis method is essentially the same as `str.replace`, with two major differences:\n\n1. If the first argument is a string, it replaces *all occurences* of the string, while `replace` replaces only the *first occurence*.\n2. If the first argument is a regular expression without the `g` flag, there'll be an error. With `g` flag, it works the same as `replace`.\n\nThe main use case for `replaceAll` is replacing all occurences of a string.\n\nLike this:\n\n```js run\n// replace all dashes by a colon\nalert('12-34-56'.replaceAll(\"-\", \":\")) // 12:34:56\n```\n\n\n## regexp.exec(str)\n\nThe `regexp.exec(str)` method returns a match for `regexp` in the string `str`.  Unlike previous methods, it's called on a regexp, not on a string.\n\nIt behaves differently depending on whether the regexp has flag `pattern:g`.\n\nIf there's no `pattern:g`, then `regexp.exec(str)` returns the first match exactly as  `str.match(regexp)`. This behavior doesn't bring anything new.\n\nBut if there's flag `pattern:g`, then:\n- A call to `regexp.exec(str)` returns the first match and saves the position immediately after it in the property `regexp.lastIndex`.\n- The next such call starts the search from position `regexp.lastIndex`, returns the next match and saves the position after it in `regexp.lastIndex`.\n- ...And so on.\n- If there are no matches, `regexp.exec` returns `null` and resets `regexp.lastIndex` to `0`.\n\nSo, repeated calls return all matches one after another, using property `regexp.lastIndex` to keep track of the current search position.\n\nIn the past, before the method `str.matchAll` was added to JavaScript, calls of `regexp.exec` were used in the loop to get all matches with groups:\n\n```js run\nlet str = 'More about JavaScript at https://javascript.info';\nlet regexp = /javascript/ig;\n\nlet result;\n\nwhile (result = regexp.exec(str)) {\n  alert( `Found ${result[0]} at position ${result.index}` );\n  // Found JavaScript at position 11, then\n  // Found javascript at position 33\n}\n```\n\nThis works now as well, although for newer browsers `str.matchAll` is usually more convenient.\n\n**We can use `regexp.exec` to search from a given position by manually setting `lastIndex`.**\n\nFor instance:\n\n```js run\nlet str = 'Hello, world!';\n\nlet regexp = /\\w+/g; // without flag \"g\", lastIndex property is ignored\nregexp.lastIndex = 5; // search from 5th position (from the comma)\n\nalert( regexp.exec(str) ); // world\n```\n\nIf the regexp has flag `pattern:y`, then the search will be performed exactly at the  position `regexp.lastIndex`, not any further.\n\nLet's replace flag `pattern:g` with `pattern:y` in the example above. There will be no matches, as there's no word at position `5`:\n\n```js run\nlet str = 'Hello, world!';\n\nlet regexp = /\\w+/y;\nregexp.lastIndex = 5; // search exactly at position 5\n\nalert( regexp.exec(str) ); // null\n```\n\nThat's convenient for situations when we need to \"read\" something from the string by a regexp at the exact position, not somewhere further.\n\n## regexp.test(str)\n\nThe method `regexp.test(str)` looks for a match and returns `true/false` whether it exists.\n\nFor instance:\n\n```js run\nlet str = \"I love JavaScript\";\n\n// these two tests do the same\nalert( *!*/love/i*/!*.test(str) ); // true\nalert( str.search(*!*/love/i*/!*) != -1 ); // true\n```\n\nAn example with the negative answer:\n\n```js run\nlet str = \"Bla-bla-bla\";\n\nalert( *!*/love/i*/!*.test(str) ); // false\nalert( str.search(*!*/love/i*/!*) != -1 ); // false\n```\n\nIf the regexp has flag `pattern:g`, then `regexp.test` looks from `regexp.lastIndex` property and updates this property, just like `regexp.exec`.\n\nSo we can use it to search from a given position:\n\n```js run\nlet regexp = /love/gi;\n\nlet str = \"I love JavaScript\";\n\n// start the search from position 10:\nregexp.lastIndex = 10;\nalert( regexp.test(str) ); // false (no match)\n```\n\n````warn header=\"Same global regexp tested repeatedly on different sources may fail\"\nIf we apply the same global regexp to different inputs, it may lead to wrong result, because `regexp.test` call advances `regexp.lastIndex` property, so the search in another string may start from non-zero position.\n\nFor instance, here we call `regexp.test` twice on the same text, and the second time fails:\n\n```js run\nlet regexp = /javascript/g;  // (regexp just created: regexp.lastIndex=0)\n\nalert( regexp.test(\"javascript\") ); // true (regexp.lastIndex=10 now)\nalert( regexp.test(\"javascript\") ); // false\n```\n\nThat's exactly because `regexp.lastIndex` is non-zero in the second test.\n\nTo work around that, we can set `regexp.lastIndex = 0` before each search. Or instead of calling methods on regexp, use string methods `str.match/search/...`, they don't use `lastIndex`.\n````\n"
  },
  {
    "path": "9-regular-expressions/index.md",
    "content": "# Regular expressions\n\nRegular expressions is a powerful way of doing search and replace in strings.\n"
  },
  {
    "path": "AUTHORING.md",
    "content": "\n# Authoring\n\nThis describes important stuff about authoring new articles of the tutorial.\n\n## Internal links\n\nAll tutorial links should start from the root, not including the domain.\n\n✅ OK:\n\n```md\nWe'll cover that in the chapter [about functions](/function-basics)\n```\n\n❌ Not ok:\n\n```md\nWe'll cover that in the chapter [about functions](https://javascript.info/function-basics)\n```\n\nAlso, to reference a chapter, there's a special \"info:\" scheme, like this:\n\n```md\nWe'll cover that in the chapter <info:function-basics>.\n```\n\nBecomes:\n```html\nWe'll cover that in the chapter <a href=\"/function-basics\">Function basics</a>.\n```\n\nThe title is auto-inserted from the referenced article. That has the benefit of keeping the right title if the article gets renamed.\n\n## TODO\n\nAsk @iliakan to for more details.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "\nThe tutorial is free to read.\n\nIf you'd like to do something else with it, please get a permission from Ilya Kantor, iliakan@javascript.info.\n\nAs of now, we license the tutorial to almost everyone for free under the terms of an open license. Just please be so kind to contact me.\n\n## License (Short)\n\nThe license is basically [CC-BY-NC](https://creativecommons.org/licenses/by-nc/4.0/legalcode), revocable and exclusive.\n\nIt gives the right to:\n- **Share** – copy and redistribute the tutorial in any medium or material.\n- **Adapt** – remix, transform, and build upon the material.\n\nUnder the following terms:\n\n- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.\n- **NonCommercial** — You may not use the material for commercial purposes.\n\n## License (Legal)\n\nBy exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this license (\"Public License\"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions.\n\n### Section 1 – Definitions.\n\na. __Adapted Material__ means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image.\n\nb. __Adapter's License__ means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License.\n\nc. __Copyright and Similar Rights__ means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights.\n\nd. __Effective Technological Measures__ means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements.\n\ne. __Exceptions and Limitations__ means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material.\n\nf. __Licensed Material__ means the artistic or literary work, database, or other material to which the Licensor applied this Public License.\n\ng. __Licensed Rights__ means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license.\n\nh. __Licensor__ means the individual(s) or entity(ies) granting rights under this Public License.\n\ni. __NonCommercial__ means not primarily intended for or directed towards commercial advantage or monetary compensation. For purposes of this Public License, the exchange of the Licensed Material for other material subject to Copyright and Similar Rights by digital file-sharing or similar means is NonCommercial provided there is no payment of monetary compensation in connection with the exchange.\n\nj. __Share__ means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them.\n\nk. __Sui Generis Database Rights__ means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world.\n\nl. __You__ means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning.\n\n### Section 2 – Scope.\n\na. ___License grant.___\n\n   1. Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to:\n\n       A. reproduce and Share the Licensed Material, in whole or in part, for NonCommercial purposes only; and\n\n       B. produce, reproduce, and Share Adapted Material for NonCommercial purposes only.\n\n   2. __Exceptions and Limitations.__ For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions.\n\n   3. __Term.__ The term of this Public License is specified in Section 6(a).\n\n   4. __Media and formats; technical modifications allowed.__ The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material.\n\n   5. __Downstream recipients.__\n\n        A. __Offer from the Licensor – Licensed Material.__ Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License.\n\n        B. __No downstream restrictions.__ You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material.\n\n   6. __No endorsement.__ Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i).\n\nb. ___Other rights.___\n\n   1. Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise.\n\n   2. Patent and trademark rights are not licensed under this Public License.\n\n   3. To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties, including when the Licensed Material is used other than for NonCommercial purposes.\n\n### Section 3 – License Conditions.\n\nYour exercise of the Licensed Rights is expressly made subject to the following conditions.\n\na. ___Attribution.___\n\n   1. If You Share the Licensed Material (including in modified form), You must:\n\n       A. retain the following if it is supplied by the Licensor with the Licensed Material:\n\n         i. identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated);\n\n         ii. a copyright notice;\n\n         iii. a notice that refers to this Public License;\n\n         iv. a notice that refers to the disclaimer of warranties;\n\n         v. a URI or hyperlink to the Licensed Material to the extent reasonably practicable;\n\n       B. indicate if You modified the Licensed Material and retain an indication of any previous modifications; and\n\n       C. indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License.\n\n   2. You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information.\n\n   3. If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable.\n\n   4. If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License.\n\n### Section 4 – Sui Generis Database Rights.\n\nWhere the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material:\n\na. for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database for NonCommercial purposes only;\n\nb. if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and\n\nc. You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database.\n\nFor the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights.\n\n### Section 5 – Disclaimer of Warranties and Limitation of Liability.\n\na. __Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You.__\n\nb. __To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You.__\n\nc. The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability.\n\n### Section 6 – Term and Termination.\n\na. This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically.\n\nb. Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates:\n\n   1. automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or\n\n   2. upon express reinstatement by the Licensor.\n\n   For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License.\n\nc. For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License.\n\nd. Sections 1, 5, 6, 7, and 8 survive termination of this Public License.\n\n### Section 7 – Other Terms and Conditions.\n\na. The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed.\n\nb. Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License.\n\n### Section 8 – Interpretation.\n\na. For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License.\n\nb. To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions.\n\nc. No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor.\n\nd. Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority.\n"
  },
  {
    "path": "README.md",
    "content": "# Tutorial JavaScript Modern\n\nIni adalah repository konten Indonesia dari Tutorial JavaScript Modern yang terbit di [https://javascript.info](https://javascript.info).\n\n## Penerjemahan\n\nKami ingin membuat tutorial tersedia dalam banyak bahasa. Silakan bantu kami menerjemahkannya.\n\nLihat <https://javascript.info/translate> untuk detilnya.\n\n## Kontribusi\n\nKami juga ingin berkolaborasi dengan orang lain di tutorial ini.\n\nApa ada yang salah? Apa ada topik yang hilang? Jelaskan ke semua orang, ajukan sebagai PR 👏\n\n**Kamu bisa edit teks di editor apapun.** Tutorial ini memakai format \"markdown\" yang canggih, mudah dipahami. Jika kamu mau lihat bagaimana tampilan on-site-nya, ada server untuk menjalankan tutorial secara lokal di <https://github.com/javascript-tutorial/server>.  \n\nDaftar kontributor tersedia di <https://javascript.info/about#contributors>.\n\n## Struktur\n\nTiap bab, artikel atau tugas mempunyai foldernya sendiri.\n\nFoldernya dinamai seperti `N-url`, di mana `N` adalah angka untuk tujuan pengurutan dan `url` adalah URL dengan judul bahan material.\n\nTipe material didefinisikan oleh file di dalam foldernya:\n\n  - `index.md` berdiri untuk sebuah bab\n  - `article.md` berdiri untuk satu artikel\n  - `task.md` berdiri untuk satu tugas (solusi harus disediakan dalam file `solution.md` juga)\n\nSetiap file di sini dimulai dari `# Main header`.\n\nSangat mudah menambah hal baru.\n\n## Tips\n\nBeberapa tips untuk penerjemah:\n\n- Referensi untuk _Markdown_ pada GitHub https://guides.github.com/features/mastering-markdown/\n- Disarankan menggunakan teks editor markdown berbasis web seperti [StackEdit](https://stackedit.io/app#), ataupun yang lainnya.\n- Jika terdapat artikel yang sudah diterjemahkan namun sulit untuk dimengerti, penerjemah dapat membandingkan artikel tersebut dengan versi bahasa inggris, ubah artikel yang menurut penerjemah sulit dimengerti lalu lakukan PR.\n\n- Terjemahan tidak harus akurat, yang terpenting mudah dipahami.\n- Jangan terjemahkan error seperti ```Uncaught ReferenceError: asdfg tidak didefinisikan```, tapi tambahkan catatan tambahan ```Uncaught ReferenceError: asdfg is not defined (asdfg belum/tidak terdefinisi/didefinisikan)```\n- Periksa [_glossary_](https://github.com/javascript-tutorial/id.javascript.info/blob/master/glossary.md) untuk melihat kata-kata yang digunakan pada repo ini, jika kata yang diinginkan tidak ada maka diskusikan pada issues #150 .\n\n## Jalankan repo secara lokal\nJika penerjemah ingin melihat repo dijalankan secara lokal, penerjemah dapat menggunakan https://github.com/javascript-tutorial/server.\n"
  },
  {
    "path": "css.md",
    "content": "\n# CSS for JS developers\n\n- Outline\n"
  },
  {
    "path": "glossary.md",
    "content": "### Kata-kata dasar yang digunakan pada repo id.javascript.info\nGunakan _glossary_ ini untuk penggunaan kata-kata yang terdapat pada _id.javascript.info_\n\nJika ingin menambahkan mohon diskusikan dulu didalam _issue_ #150\n\n## A\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nArray   | Senarai   | _Array_\nAds     | Iklan     | Iklan\nasynchronous | Asinkronus | Asinkronus\n\n## B\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nBrowser | Peramban  | _Browser_\n\n## C\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nCode    | Kode      | Kode\nCarousel| Korsel    | Carousel\n\n\n## E\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nEvent   | Peristiwa | _Event_\nError   | Galat     | Error\n\n## I\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nInput   | Masukan   | _Input_\n\n## P\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nPopup   | Muncul    | _Popup_\n\n## R\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nRequest | Permintaan| _Request_\n\n## S\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nSummary | Ringkasan | Ringkasan\nSyntax  | Sintaks   | Sintaks\nSynchronously | Sinkronus | Sinkronus\n\n## W\nInggris | Indonesia | Gunakan\n------- | --------- | -------\nWindow  | Jendela   | _Window_\n\n"
  },
  {
    "path": "script/clean-unused-png.php",
    "content": "<?php\n$files = trim(`find . -name *.png`);\n$files = explode(\"\\n\", $files);\n\n$root = getcwd();\n\necho $root;\nforeach($files as $file) {\n  $file = trim(substr($file, 1));\n  if (strstr($file, '@2x')) continue;\n\n  $filename = basename($file);\n  $dir = $root . dirname($file);\n  chdir($dir);\n  $result = `grep $filename *`;\n\n  if (!$result) {\n    echo $dir, \"\\n\", $filename;\n\n    exit;\n  }\n}\n"
  },
  {
    "path": "todo.md",
    "content": "\n- Drag events\n- History api\n- Pointer events\n- Touch events\n- Canvas (2d graphics)\n- Security (xsrf xss csp etc)\n"
  }
]