From 3dc9373475c80f39bd063408934f2b1bea85547e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 14 Jan 2020 09:44:35 +0100 Subject: [PATCH 001/242] Remove confusing phrasing Issue #510 --- 11_async.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/11_async.md b/11_async.md index c916ed79a..b184d7a0f 100644 --- a/11_async.md +++ b/11_async.md @@ -515,9 +515,8 @@ function request(nest, target, type, content) { Because promises can be resolved (or rejected) only once, this will work. The first time `resolve` or `reject` is called determines the -outcome of the promise, and any further calls, such as the timeout -arriving after the request finishes or a request coming back after -another request finished, are ignored. +outcome of the promise, and further calls caused by a request coming +back after another request finished are ignored. {{index recursion}} From 9ae2daec41ad8256a2cc1ec3b64af597edb971a6 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 15 Jan 2020 08:06:59 +0100 Subject: [PATCH 002/242] Fix inconsistency between definition of `request` and text Closes #511 --- 11_async.md | 4 ++-- html/errata.html | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/11_async.md b/11_async.md index b184d7a0f..1076173f2 100644 --- a/11_async.md +++ b/11_async.md @@ -525,13 +525,13 @@ recursive function—a regular loop doesn't allow us to stop and wait for an asynchronous action. The `attempt` function makes a single attempt to send a request. It also sets a timeout that, if no response has come back after 250 milliseconds, either starts the next attempt -or, if this was the fourth attempt, rejects the promise with an +or, if this was the third attempt, rejects the promise with an instance of `Timeout` as the reason. {{index idempotence}} Retrying every quarter-second and giving up when no response has come -in after a second is definitely somewhat arbitrary. It is even +in after three-quarter second is definitely somewhat arbitrary. It is even possible, if the request did come through but the handler is just taking a bit longer, for requests to be delivered multiple times. We'll write our handlers with that problem in mind—duplicate messages diff --git a/html/errata.html b/html/errata.html index 00389b522..081e11bc4 100644 --- a/html/errata.html +++ b/html/errata.html @@ -58,6 +58,13 @@

Chapter 10

needs it own private scope“, it should say “its own private scope“.

+

Chapter 11

+ +

Page 189/190 Networks are Hard: The text +under the code that defines request claims the function +will give up after four attempts and a second. In fact, it gives up +after three attempts, in three-quarter second.

+

Chapter 14

Page 234 (2nd) Creating Nodes: In the From 5b6a42408126f945b9ef81a4c2a0e3072caa3b90 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 16 Jan 2020 14:55:23 +0100 Subject: [PATCH 003/242] Avoid calling string values objects --- 04_data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/04_data.md b/04_data.md index 15b5768bc..52bc92c7e 100644 --- a/04_data.md +++ b/04_data.md @@ -189,7 +189,7 @@ advance, so to find the length of an array, you typically write {{index [function, "as property"], method, string}} -Both string and array objects contain, in addition to the `length` +Both string and array values contain, in addition to the `length` property, a number of properties that hold function values. ``` From 175b28c92e94ea2cdb35e4609747c87d4767f67e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 4 Apr 2020 23:03:07 +0200 Subject: [PATCH 004/242] Fix linking of zip files from code/index.html --- 20_node.md | 2 +- 21_skillsharing.md | 2 +- src/chapter_info.js | 13 +++++++------ 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/20_node.md b/20_node.md index 88b8bca38..b85661be3 100644 --- a/20_node.md +++ b/20_node.md @@ -1,4 +1,4 @@ -{{meta {code_links: "[\"code/file_server.js\"]"}}} +{{meta {code_links: ["code/file_server.js"]}}} # Node.js diff --git a/21_skillsharing.md b/21_skillsharing.md index 64718e188..71cac0bda 100644 --- a/21_skillsharing.md +++ b/21_skillsharing.md @@ -1,4 +1,4 @@ -{{meta {code_links: "[\"code/skillsharing.zip\"]"}}} +{{meta {code_links: ["code/skillsharing.zip"]}}} # Project: Skill-Sharing Website diff --git a/src/chapter_info.js b/src/chapter_info.js index 0a08e4036..294570156 100644 --- a/src/chapter_info.js +++ b/src/chapter_info.js @@ -23,7 +23,7 @@ for (let file of fs.readdirSync(".").sort()) { start_code: getStartCode(text, includes), exercises: [], include: includes}; - let zip = chapterZipFile(text, chapter); + let zip = chapterZipFile(meta, chapter); let extraLinks = meta.match(/\bcode_links: (\[.*?\])/); if (extraLinks) extraLinks = JSON.parse(extraLinks[1]); if (extraLinks || zip) @@ -180,12 +180,13 @@ function getStartCode(text, includes) { return snippet; } -function chapterZipFile(text, chapter) { - let spec = text.match(/\n:zip: (\S+)(?: include=(.*))?/); +function chapterZipFile(meta, chapter) { + let spec = meta.match(/\bzip: ("(?:\\.|[^"\\])*")/); if (!spec) return null; if (!chapter.start_code) throw new Error("zip but no start code"); + let data = /(\S+)(?:\s+include=(.*))?/.exec(JSON.parse(spec[1])) let name = "code/chapter/" + chapter.id + ".zip"; - let files = (chapter.include || []).concat(spec[2] ? JSON.parse(spec[2]) : []); + let files = (chapter.include || []).concat(data[2] ? JSON.parse(data[2]) : []); let exists = fs.existsSync(name) && fs.statSync(name).mtime; if (exists && files.every(file => fs.statSync("html/" + file).mtime < exists)) return name; @@ -194,13 +195,13 @@ function chapterZipFile(text, chapter) { for (let file of files) { zip.file(chapter.id + "/" + file, fs.readFileSync("html/" + file)); } - if (spec[1].indexOf("html") != -1) { + if (data[1].indexOf("html") != -1) { let html = chapter.start_code; if (guessType(html) != "html") html = prepareHTML("", chapter.include); zip.file(chapter.id + "/index.html", html); } - if (spec[1].indexOf("node") != -1) { + if (data[1].indexOf("node") != -1) { zip.file(chapter.id + "/code/load.js", fs.readFileSync("code/load.js", "utf8")); let js = chapter.start_code; if (chapter.include) js = "// load dependencies\nrequire(\"./code/load\")(" + chapter.include.map(JSON.stringify).join(", ") + ");\n\n" + js; From cfa509f373ce8ba0658a9bca1947a0fae6b70f95 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Mon, 20 Apr 2020 22:12:18 +0200 Subject: [PATCH 005/242] Add MIT license text --- code/LICENSE | 19 +++++++++++++++++++ epub/frontmatter.xhtml | 2 +- html/index.html | 2 +- pdf/book.tex | 2 +- 4 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 code/LICENSE diff --git a/code/LICENSE b/code/LICENSE new file mode 100644 index 000000000..b03a229d6 --- /dev/null +++ b/code/LICENSE @@ -0,0 +1,19 @@ +Copyright (C) 2008-2020 by Marijn Haverbeke + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/epub/frontmatter.xhtml b/epub/frontmatter.xhtml index 9a80e8a0b..81b48445c 100644 --- a/epub/frontmatter.xhtml +++ b/epub/frontmatter.xhtml @@ -15,7 +15,7 @@ a Creative Commons attribution-noncommercial license. All code in this book may also be considered licensed under - an MIT license.

+ an MIT license.

Illustrations by various artists: Cover and chapter illustrations by Madalina Tantareanu. Pixel art in diff --git a/html/index.html b/html/index.html index c7702175a..e10e65d37 100644 --- a/html/index.html +++ b/html/index.html @@ -41,7 +41,7 @@

Eloquent JavaScript
3rd edition

a Creative Commons attribution-noncommercial license. All code in this book may also be considered licensed under - an MIT license.

+ an MIT license.

Illustrations by various artists: Cover and chapter illustrations by Madalina Tantareanu. diff --git a/pdf/book.tex b/pdf/book.tex index 1b8dbaa67..7f0ce7ad4 100644 --- a/pdf/book.tex +++ b/pdf/book.tex @@ -95,7 +95,7 @@ attribution-noncommercial license (\url{http://creativecommons.org/licenses/by-nc/3.0/}). All code in the book may also be considered licensed under an MIT license - (\url{http://opensource.org/licenses/MIT}). + (\url{https://eloquentjavascript.net/code/LICENSE}). The illustrations are contributed by various artists: Cover and chapter illustrations by Madalina Tantareanu. Pixel art in Chapters From 7ce94f406895a01a4c7ddcb5f27cf5e56a091ab3 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 24 May 2020 21:38:32 +0200 Subject: [PATCH 006/242] Fix incorrect chapter number in frontmatter --- epub/frontmatter.xhtml | 2 +- html/index.html | 2 +- pdf/book.tex | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/epub/frontmatter.xhtml b/epub/frontmatter.xhtml index 81b48445c..604723fdb 100644 --- a/epub/frontmatter.xhtml +++ b/epub/frontmatter.xhtml @@ -23,7 +23,7 @@ diagrams in Chapter 9 generated with regexper.com by Jeff Avallone. Village photograph in Chapter 11 by Fabrice Creuzot. - Game concept for Chapter 15 + Game concept for Chapter 16 by Thomas Palef.

The third edition was made possible diff --git a/html/index.html b/html/index.html index e10e65d37..d7c40cd03 100644 --- a/html/index.html +++ b/html/index.html @@ -49,7 +49,7 @@

Eloquent JavaScript
3rd edition

expression diagrams in Chapter 9 generated with regexper.com by Jeff Avallone. Village photograph in Chapter 11 by Fabrice Creuzot. Game - concept for Chapter 15 by Thomas + concept for Chapter 16 by Thomas Palef.

The third edition was made possible diff --git a/pdf/book.tex b/pdf/book.tex index 7f0ce7ad4..cef6062fa 100644 --- a/pdf/book.tex +++ b/pdf/book.tex @@ -102,7 +102,7 @@ 7 and 16 by Antonio Perdomo Pastor. Regular expression diagrams in Chapter 9 generated with \href{http://regexper.com}{regexper.com} by Jeff Avallone. Village photograph in Chapter 11 by Fabrice Creuzot. - Game concept for Chapter 15 by \href{http://lessmilk.com}{Thomas + Game concept for Chapter 16 by \href{http://lessmilk.com}{Thomas Palef}. The third edition of Eloquent JavaScript was made possible From 08f0f2a898f08a172ed78264c3dc2869054c20ad Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 26 May 2020 10:06:31 +0200 Subject: [PATCH 007/242] Add parens after new expression in Chapter 11 For consistency --- 11_async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/11_async.md b/11_async.md index 1076173f2..9ee8bd96b 100644 --- a/11_async.md +++ b/11_async.md @@ -747,7 +747,7 @@ function broadcastConnections(nest, name, exceptFor = null) { } everywhere(nest => { - nest.state.connections = new Map; + nest.state.connections = new Map(); nest.state.connections.set(nest.name, nest.neighbors); broadcastConnections(nest, nest.name); }); From 00cc6ce5c0f16e0fdb4d73f0b9b3a19b3700f00e Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Tue, 26 May 2020 10:07:25 +0200 Subject: [PATCH 008/242] Mark one errata as fixed in the 4th reprint --- html/errata.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/html/errata.html b/html/errata.html index 081e11bc4..79df33b60 100644 --- a/html/errata.html +++ b/html/errata.html @@ -60,7 +60,7 @@

Chapter 10

Chapter 11

-

Page 189/190 Networks are Hard: The text +

Page 189/190 (4th) Networks are Hard: The text under the code that defines request claims the function will give up after four attempts and a second. In fact, it gives up after three attempts, in three-quarter second.

From f6ef8c46539554c00e790cb41e7e6fff31b24484 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sun, 14 Jun 2020 18:18:30 +0200 Subject: [PATCH 009/242] Fix misnamed property in Chapter 19 --- 19_paint.md | 4 ++-- html/errata.html | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/19_paint.md b/19_paint.md index 84297e2bf..3bc43f266 100644 --- a/19_paint.md +++ b/19_paint.md @@ -183,14 +183,14 @@ number to create an empty array of the given length. The `fill` method can then be used to fill this array with a given value. These are used to create an array in which all pixels have the same color. -{{index "hexadecimal number", "color component", "color field", "fillColor property"}} +{{index "hexadecimal number", "color component", "color field", "fillStyle property"}} Colors are stored as strings containing traditional ((CSS)) ((color code))s made up of a ((hash sign)) (`#`) followed by six hexadecimal (base-16) digits—two for the ((red)) component, two for the ((green)) component, and two for the ((blue)) component. This is a somewhat cryptic and inconvenient way to write colors, but it is the format the -HTML color input field uses, and it can be used in the `fillColor` +HTML color input field uses, and it can be used in the `fillStyle` property of a canvas drawing context, so for the ways we'll use colors in this program, it is practical enough. diff --git a/html/errata.html b/html/errata.html index 79df33b60..ff665fb86 100644 --- a/html/errata.html +++ b/html/errata.html @@ -85,6 +85,11 @@

Chapter 16

refers to the arrow binding, where it should say arrowKeys.

+

Chapter 19

+ +

Page 336 The State: The text mentions the +property `fillColor` where it should say `fillStyle` instead. +

Chapter 20

Page 369 (1st) Directory From 996c2bc3ad626eb5d9e2eaf8456fa77fef73c4cf Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 9 Jul 2020 17:40:10 +0200 Subject: [PATCH 010/242] Add a cache-control header to Chapter 21 responses To work around a Chrome issue where it'd hold up a request in one tab when another tab was still polling the same URL. --- 21_skillsharing.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/21_skillsharing.md b/21_skillsharing.md index 71cac0bda..d53308d7e 100644 --- a/21_skillsharing.md +++ b/21_skillsharing.md @@ -570,7 +570,8 @@ SkillShareServer.prototype.talkResponse = function() { return { body: JSON.stringify(talks), headers: {"Content-Type": "application/json", - "ETag": `"${this.version}"`} + "ETag": `"${this.version}"`, + "Cache-Control": "no-store"} }; }; ``` From bdff0df4fcf4e6448de5feff97ddc973aeb59164 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Thu, 30 Jul 2020 08:12:35 +0200 Subject: [PATCH 011/242] Fix a mistake in the solution to ex. 21.2 --- code/solutions/21_2_comment_field_resets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/solutions/21_2_comment_field_resets.js b/code/solutions/21_2_comment_field_resets.js index 57bbf1338..2c345d33a 100644 --- a/code/solutions/21_2_comment_field_resets.js +++ b/code/solutions/21_2_comment_field_resets.js @@ -56,7 +56,7 @@ class SkillShareApp { for (let talk of state.talks) { let cmp = this.talkMap[talk.title]; - if (cmp && cmp.talk.author == talk.author && + if (cmp && cmp.talk.presenter == talk.presenter && cmp.talk.summary == talk.summary) { cmp.syncState(talk); } else { From 7896534f94988f244b3dfa853ecbbd0a9378535d Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Wed, 26 Aug 2020 09:08:36 +0200 Subject: [PATCH 012/242] Drop use of 'hottentottententen' Due to colonial connotations --- 09_regexp.md | 4 ++-- code/solutions/09_1_regexp_golf.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/09_regexp.md b/09_regexp.md index f8f8291f0..75307dc9e 100644 --- a/09_regexp.md +++ b/09_regexp.md @@ -1305,8 +1305,8 @@ verify(/.../, ["escape the period"]); verify(/.../, - ["hottentottententen"], - ["no", "hotten totten tenten"]); + ["Siebentausenddreihundertzweiundzwanzig"], + ["no", "three small words"]); verify(/.../, ["red platypus", "wobbling nest"], diff --git a/code/solutions/09_1_regexp_golf.js b/code/solutions/09_1_regexp_golf.js index 72a6af16a..402450353 100644 --- a/code/solutions/09_1_regexp_golf.js +++ b/code/solutions/09_1_regexp_golf.js @@ -21,8 +21,8 @@ verify(/\s[.,:;]/, ["escape the dot"]); verify(/\w{7}/, - ["hottentottententen"], - ["no", "hotten totten tenten"]); + ["Siebentausenddreihundertzweiundzwanzig"], + ["no", "three small words"]); verify(/\b[^\We]+\b/i, ["red platypus", "wobbling nest"], From fe5c717570bcfa9069344d6d764f848df6915735 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Sat, 5 Sep 2020 11:09:14 +0200 Subject: [PATCH 013/242] Add link to Arabic translation --- html/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/html/index.html b/html/index.html index d7c40cd03..ac484f5f7 100644 --- a/html/index.html +++ b/html/index.html @@ -137,6 +137,7 @@

Third Edition

Second Edition