├── .gitignore ├── 00_intro.txt ├── 01_values.txt ├── 02_program_structure.txt ├── 03_functions.txt ├── 04_data.txt ├── 05_higher_order.txt ├── 06_object.txt ├── 07_elife.txt ├── 08_error.txt ├── 09_regexp.txt ├── 10_modules.txt ├── 11_language.txt ├── 12_browser.txt ├── 13_dom.txt ├── 14_event.txt ├── 15_game.txt ├── 16_canvas.txt ├── 17_http.txt ├── 18_forms.txt ├── 19_paint.txt ├── 20_node.txt ├── 21_skillsharing.txt ├── Makefile ├── README.md ├── asciidoc_epub.conf ├── asciidoc_html.conf ├── asciidoc_nostarch.conf ├── asciidoc_pdf.conf ├── bin ├── .DS_Store ├── .tern-project ├── add_images_to_epub.js ├── addmarks ├── blockfilter ├── build_code.js ├── chapter_info.js ├── check_links.js ├── clean_latex.js ├── extract_hints.js ├── pre_epub.js ├── pre_latex.js ├── run_tests.js └── wrap.tex ├── code ├── ancestry.js ├── animateworld.js ├── chapter │ ├── .keep │ └── 22_fast.js ├── draw_graph.js ├── file_server_promises.js ├── game_levels.js ├── hello.js ├── index.html ├── intro.js ├── jacques_journal.js ├── load.js ├── loadfile.js ├── mountains.js ├── promise.js ├── skillshare.zip ├── skillsharing │ ├── .keep │ ├── package.json │ └── public │ │ └── skillsharing.css ├── solutions │ ├── 02_1_iterando_un_tringulo.js │ ├── 02_2_fizzbuzz.js │ ├── 02_3_tablero_de_ajedrez.js │ ├── 03_1_mnimo.js │ ├── 03_2_recursin.js │ ├── 03_3_contando_frijoles.js │ ├── 04_1_the_sum_of_a_range.js │ ├── 04_2_reversing_an_array.js │ ├── 04_3_a_list.js │ ├── 04_4_deep_comparison.js │ ├── 05_1_flattening.js │ ├── 05_2_mother-child_age_difference.js │ ├── 05_3_historical_life_expectancy.js │ ├── 05_4_every_and_then_some.js │ ├── 06_1_un_tipo_vector.js │ ├── 06_2_otra_celda.js │ ├── 06_3_interface_secuencia.js │ ├── 07_1_artificial_stupidity.js │ ├── 07_2_predators.js │ ├── 08_1_retry.js │ ├── 08_2_the_locked_box.js │ ├── 09_1_regexp_golf.js │ ├── 09_2_quoting_style.js │ ├── 09_3_numbers_again.js │ ├── 10_1_month_names.js │ ├── 11_1_arrays.js │ ├── 11_3_comments.js │ ├── 11_4_fixing_scope.js │ ├── 13_1_build_a_table.html │ ├── 13_2_elements_by_tag_name.html │ ├── 13_3_the_cats_hat.html │ ├── 14_1_censored_keyboard.html │ ├── 14_2_mouse_trail.html │ ├── 14_3_tabs.html │ ├── 15_1_game_over.html │ ├── 15_2_pausing_the_game.html │ ├── 16_1_shapes.html │ ├── 16_2_the_pie_chart.html │ ├── 16_3_a_bouncing_ball.html │ ├── 17_1_content_negotiation.js │ ├── 17_2_waiting_for_multiple_promises.js │ ├── 18_1_a_javascript_workbench.html │ ├── 18_2_autocompletion.html │ ├── 18_3_conways_game_of_life.html │ ├── 19_1_rectangles.html │ ├── 19_2_color_picker.html │ ├── 19_3_flood_fill.html │ ├── 20_1_content_negotiation_again.js │ ├── 20_2_fixing_a_leak.js │ ├── 20_3_creating_directories.js │ ├── 20_4_a_public_space_on_the_web │ │ ├── index.html │ │ ├── other.html │ │ └── public_space.js │ ├── 21_1_disk_persistence.js │ ├── 21_2_comment_field_resets.js │ ├── 21_3_better_templates.js │ ├── 22_1_pathfinding.js │ ├── 22_2_timing.js │ └── 22_3_optimizing.js └── squareworker.js ├── epub ├── .DS_Store ├── META-INF │ └── container.xml ├── content.opf.src ├── font │ ├── cinzel_bold.otf │ └── pt_mono.otf ├── frontmatter.xhtml ├── mimetype ├── style.css ├── titlepage.xhtml └── toc.xhtml ├── html ├── .DS_Store ├── .keep ├── author.html ├── author.json ├── author.txt ├── backers.html ├── code ├── css │ ├── ejs.css │ ├── game.css │ └── paint.css ├── empty.html ├── errata.html ├── example │ ├── .DS_Store │ ├── bert.json │ ├── data.txt │ ├── fruit.json │ ├── fruit.xml │ ├── message.html │ ├── muriel.json │ ├── submit.html │ └── suzie.json ├── favicon.ico ├── font │ ├── cinzel_bold.woff │ └── pt_mono.woff ├── img ├── index.html └── js │ ├── .tern-project │ ├── code.js │ ├── ejs.js │ ├── node_modules │ └── sandbox.js ├── img ├── .DS_Store ├── alert.png ├── bit-sea.png ├── blockquote.png ├── boxed-in.png ├── button_disabled.png ├── canvas_arc.png ├── canvas_beziercurve.png ├── canvas_circle.png ├── canvas_fill.png ├── canvas_game.png ├── canvas_path.png ├── canvas_pie_chart.png ├── canvas_quadraticcurve.png ├── canvas_scale.png ├── canvas_stroke.png ├── canvas_tree.png ├── canvas_triangle.png ├── cat-animation.png ├── cat.png ├── color-field.png ├── colored-links.png ├── computer.svg ├── confirm.png ├── control-io.svg ├── controlflow-else.svg ├── controlflow-if.svg ├── controlflow-loop.svg ├── controlflow-nested-if.svg ├── controlflow-straight.svg ├── cos_sin.svg ├── cover.jpg ├── cover_.png ├── darkblue.png ├── display.png ├── drag-bar.png ├── exercise_shapes.png ├── flood-grid.svg ├── form_fields.png ├── form_multi_select.png ├── form_select.png ├── game-grid.svg ├── game_simpleLevel.png ├── generated │ └── .keep ├── ghostery.png ├── ghostery_mini.png ├── hack_reactor.png ├── hack_reactor_mini.png ├── hat.png ├── help-field.png ├── highlighted.png ├── home-page.png ├── html-boxes.svg ├── html-links.svg ├── html-tree.svg ├── linked-list.svg ├── mirror.svg ├── mozilla.png ├── mozilla_mini.png ├── object.jpg ├── object_full.jpg ├── octopus-array-big.jpg ├── octopus-array.jpg ├── octopus-big.jpg ├── octopus-object-big.jpg ├── octopus-object.jpg ├── octopus.jpg ├── ostrich.png ├── paint.png ├── pizza-squirrel.svg ├── player.png ├── player_big.png ├── prompt.png ├── rabbits.svg ├── re_number.svg ├── re_pigchickens.svg ├── re_slow.svg ├── skillsharing.png ├── sprites.png ├── sprites_big.png ├── svg-demo.png ├── syntax_tree.svg ├── transform.svg ├── unicycle.svg ├── weresquirrel.png └── weresquirrel.svg ├── mkcopy ├── nostarch ├── book.tex ├── build.sh ├── nostarch.cls ├── nostarch.ins ├── nostarch.ist └── nshyper.sty ├── package.json └── pdf ├── .DS_Store ├── book.tex └── build.sh /.gitignore: -------------------------------------------------------------------------------- 1 | /nostarch/[012]*.tex 2 | /nostarch/hints.tex 3 | /nostarch/book.* 4 | /nostarch.pdf 5 | /pdf/[012]*.tex 6 | /pdf/hints.tex 7 | /pdf/book.* 8 | /pdf/book_mobile.* 9 | /book.pdf 10 | /book_mobile.pdf 11 | /html/[012]*.html 12 | /html/js/chapter_info.js 13 | /html/js/acorn_codemirror.js 14 | /code/chapter/* 15 | /code/file_server.js 16 | /code/skillsharing.zip 17 | /code/solutions/20_4_a_public_space_on_the_web.zip 18 | /code/skillsharing/* 19 | /node_modules 20 | .tern-port 21 | /toc.txt 22 | /img/generated/* 23 | /img/cover.xcf 24 | /epub/[012]*.xhtml 25 | /epub/hints.xhtml 26 | /epub/img/* 27 | /epub/content.opf 28 | /book.epub 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Eloquent JavaScript 2 | 3 | These are the sources used to build the second edition of Eloquent 4 | JavaScript (http://eloquentjavascript.net). 5 | 6 | Feedback welcome, in the form of issues and pull requests. 7 | 8 | ## Building 9 | 10 | npm install --production 11 | apt-get install asciidoc inkscape 12 | make html 13 | 14 | For OSX, you can use port or brew to install the asciidoc package. 15 | 16 | To build the PDF file: 17 | 18 | apt-get install texlive texlive-xetex texlive-fonts-extra 19 | make book.pdf 20 | 21 | To build the ePub book: 22 | 23 | make book.epub 24 | -------------------------------------------------------------------------------- /asciidoc_epub.conf: -------------------------------------------------------------------------------- 1 | [miscellaneous] 2 | newline=\n 3 | 4 | [attributes] 5 | disable-javascript=True 6 | warnings=False 7 | highlight=False 8 | book_target=True 9 | html_target=True 10 | encoding=UTF-8 11 | sectids! 12 | 13 | lsquo=‘ 14 | rsquo=’ 15 | ldquo=“ 16 | rdquo=” 17 | 18 | [quotes] 19 | `=#monospaced 20 | 21 | [macros] 22 | (?su)(?[^\]]*?)\]=indexsee 23 | \(!html (?P(?:.|\n)*?)!\)=included 24 | \(!tex (?P(?:.|\n)*?)!\)=excluded 25 | \(!interactive (?P(?:.|\n)*?)!\)=excluded 26 | \(!book (?P(?:.|\n)*?)!\)=included 27 | (?su)(?[^(](?:[^()]|\n)*?)\)\)\)(?!\))=indexterm 28 | (?[^\s\(][^(](?:[^()]|\n)*?)\)\)(?!\))=indexterm2 29 | 30 | [replacements] 31 | (\w)\'(\w)=\1’\2 32 | (? 36 | | 37 | 38 | 39 | [source-filter-style] 40 | source-style=template="source-highlight-block",presubs=(),postsubs=("callouts",),posattrs=("style","language","src_numbered","src_tab"),filter="./bin/blockfilter -s {language}" 41 | 42 | [blockdef-listing] 43 | template::[source-filter-style] 44 | 45 | [listingblock] 46 | 47 | | 48 | 49 | 50 | [indexsee-inlinemacro] 51 | {empty} 52 | 53 | [included-inlinemacro] 54 | {text} 55 | 56 | [excluded-inlinemacro] 57 | {empty} 58 | 59 | [header] 60 | 61 | 62 | 63 | {doctitle} 64 | 65 | 66 | 67 |
68 | 69 |

{chap_num?Chapter {chap_num}}{doctitle}

70 | 71 | [footer] 72 |
73 | 74 | 75 | 76 | [preamble] 77 | | 78 | 79 | [paragraph] 80 | 81 | | 82 |

83 | 84 | [tags] 85 | monospaced=| 86 | 87 | [literal-inlinemacro] 88 | {passtext} 89 | 90 | [sect1] 91 | {title} 92 | | 93 | 94 | [sect2] 95 | {title} 96 | | 97 | 98 | [sect3] 99 | {title} 100 | | 101 | 102 | [quoteblock] 103 |
104 | | 105 | {attribution?
{attribution}{citetitle?, {citetitle}}
} 106 |
107 | 108 | [image-blockmacro] 109 |
110 | {alt={target}} 111 |
112 | 113 | [table] 114 | 115 | {headrows#} 116 | {headrows} 117 | {headrows#} 118 | {footrows#} 119 | {footrows} 120 | {footrows#} 121 | {bodyrows} 122 | 123 | 124 | [tabletags-default] 125 | bodyrow=| 126 | headdata=| 127 | bodydata=| 128 | paragraph=| 129 | -------------------------------------------------------------------------------- /asciidoc_html.conf: -------------------------------------------------------------------------------- 1 | [miscellaneous] 2 | newline=\n 3 | 4 | [attributes] 5 | disable-javascript=True 6 | warnings=False 7 | highlight=False 8 | html_target=True 9 | interactive_target=True 10 | encoding=UTF-8 11 | sectids! 12 | 13 | lsquo=‘ 14 | rsquo=’ 15 | ldquo=“ 16 | rdquo=” 17 | 18 | [quotes] 19 | `=#monospaced 20 | 21 | [macros] 22 | (?su)(?[^\]]*?)\]=indexsee 23 | \(!html (?P(?:.|\n)*?)!\)=included 24 | \(!tex (?P(?:.|\n)*?)!\)=excluded 25 | \(!interactive (?P(?:.|\n)*?)!\)=included 26 | \(!book (?P(?:.|\n)*?)!\)=excluded 27 | (?su)(?[^(](?:[^()]|\n)*?)\)\)\)(?!\))=indexterm 28 | (?[^\s\(][^(](?:[^()]|\n)*?)\)\)(?!\))=indexterm2 29 | 30 | [replacements] 31 | (\w)\'(\w)=\1’\2 32 | (?
46 | | 47 |
48 | 49 | [source-highlight-block] 50 | 51 | | 52 | 53 | 54 | [source-filter-style] 55 | source-style=template="source-highlight-block",presubs=(),postsubs=("callouts",),posattrs=("style","language","src_numbered","src_tab"),filter="./bin/blockfilter -s {language}" 56 | 57 | [blockdef-listing] 58 | template::[source-filter-style] 59 | 60 | [listingblock] 61 | 62 | | 63 | 64 | 65 | [indexsee-inlinemacro] 66 | {empty} 67 | 68 | [included-inlinemacro] 69 | {text} 70 | 71 | [excluded-inlinemacro] 72 | {empty} 73 | 74 | [header] 75 | 76 | 77 | 78 | {doctitle} :: Eloquent JavaScript 79 | 80 | 81 | 82 | 83 | 84 | 85 | 95 | 96 | 97 |
98 | 103 | 104 | {chap_num?
Capítulo {chap_num}
}{doctitle} 105 | 106 | [footer] 107 | 112 |
113 | 114 | [preamble] 115 | | 116 | 117 | [paradef-default] 118 | normal-style=template="paragraph" 119 | 120 | [paragraph] 121 | 122 | | 123 |

124 | 125 | [tags] 126 | monospaced=| 127 | 128 | [literal-inlinemacro] 129 | {passtext} 130 | 131 | [sect1] 132 | {title} 133 | | 134 | 135 | [sect2] 136 | {title} 137 | | 138 | 139 | [sect3] 140 | {title} 141 | | 142 | 143 | [quoteblock] 144 |
145 | | 146 | {attribution?
{attribution}{citetitle?, {citetitle}}
} 147 |
148 | 149 | [image-blockmacro] 150 |
151 | {alt={target}} 152 |
153 | 154 | [table] 155 | 156 | {headrows#} 157 | {headrows} 158 | {headrows#} 159 | {footrows#} 160 | {footrows} 161 | {footrows#} 162 | {bodyrows} 163 | 164 | 165 | [tabletags-default] 166 | bodyrow=| 167 | headdata=| 168 | bodydata=| 169 | paragraph=| 170 | -------------------------------------------------------------------------------- /asciidoc_nostarch.conf: -------------------------------------------------------------------------------- 1 | [miscellaneous] 2 | newline=\n 3 | 4 | [attributes] 5 | sectids! 6 | highlight=False 7 | book_target=True 8 | tex_target=True 9 | commercial_target=True 10 | lsquo=‘ 11 | rsquo=’ 12 | ldquo=“ 13 | rdquo=” 14 | 15 | [replacements] 16 | “=`` 17 | ”='' 18 | ‘=` 19 | ’=' 20 | —=--- 21 | 22 | [macros] 23 | (?su)(?[^\]]*?)\]=indexsee 24 | \(!html (?P(?:.|\n)*?)!\)=excluded 25 | \(!tex (?P(?:.|\n)*?)!\)=included 26 | \(!interactive (?P(?:.|\n)*?)!\)=excluded 27 | \(!book (?P(?:.|\n)*?)!\)=included 28 | (?su)(?[^(](?:[^()]|\n)*?)\)\)\)(?!\))=indexterm 29 | (?[^\s\(][^(](?:[^()]|\n)*?)\)\)(?!\))=indexterm2 30 | 31 | [tags] 32 | superscript=^!..braceleft..!|!..braceright..! 33 | subscript=_!..braceleft..!|!..braceright..! 34 | 35 | [quotes] 36 | `=#monospaced 37 | 38 | [link-inlinemacro] 39 | !..backslash..!hyperref[{target}]!..braceleft..!{0}!..braceright..! 40 | 41 | [indexsee-inlinemacro] 42 | !..backslash..!index!..braceleft..!{1}|see!..braceleft..!{2}!..braceright..!!..braceright..! 43 | 44 | [http-inlinemacro] 45 | !..backslash..!href!..braceleft..!http:{target}!..braceright..!!..braceleft..!{0}!..braceright..! 46 | 47 | [blockdef-listing] 48 | source-style=template="codeblock" 49 | 50 | [excluded-inlinemacro] 51 | {empty} 52 | 53 | [included-inlinemacro] 54 | {text} 55 | 56 | [image-blockmacro] 57 | \vskip 1.5ex 58 | \includegraphics[width={width=10cm}]\{{target}\} 59 | \vskip 1.5ex 60 | {newline} 61 | \noindent 62 | 63 | [partintroblock] 64 | template::[openblock] 65 | 66 | [openblock] 67 | | 68 | 69 | [quoteblock] 70 | {chapterquote?\epigraphhead[30]\{ } 71 | {chapterquote!\begin\{quote\}} 72 | {chapterquote?\epigraph\{\hspace*\{-.1cm\}\itshape``}|{chapterquote?''\}%} 73 | {attribution?\{---{attribution}{citetitle?, \emph\{{citetitle}\}}\}} 74 | {chapterquote?\}} 75 | {chapterquote!\end\{quote\}} 76 | {newline} 77 | 78 | [codeblock] 79 | \label\{{id}\} 80 | \begin\{Code\}{newline} 81 | |{newline} 82 | \end\{Code\} 83 | {newline} 84 | 85 | [listingblock] 86 | \begin\{Code\}{newline} 87 | |{newline} 88 | \end\{Code\} 89 | {newline} 90 | 91 | [paragraph] 92 | \label\{{id}\} 93 | | 94 | {newline} 95 | 96 | [header] 97 | \chapter\{{doctitle}\} 98 | {docid?\label\{{docid}\}} 99 | 100 | [sect1] 101 | \section\{{title}\} 102 | \label\{{id}\} 103 | {newline} 104 | | 105 | 106 | [sect2] 107 | \subsection\{{title}\} 108 | \label\{{id}\} 109 | {newline} 110 | | 111 | 112 | [sect3] 113 | \subsubsection\{{title}\} 114 | \label\{{id}\} 115 | {newline} 116 | | 117 | 118 | [footer] 119 | {empty} 120 | 121 | [table] 122 | \noindent\begin\{tabular\}\{ll\} 123 | {headrows} 124 | {bodyrows} 125 | {footrows} 126 | \end\{tabular\} 127 | {newline} 128 | \noindent 129 | -------------------------------------------------------------------------------- /asciidoc_pdf.conf: -------------------------------------------------------------------------------- 1 | [miscellaneous] 2 | newline=\n 3 | 4 | [attributes] 5 | sectids! 6 | highlight=False 7 | book_target=True 8 | tex_target=True 9 | lsquo=‘ 10 | rsquo=’ 11 | ldquo=“ 12 | rdquo=” 13 | 14 | [replacements] 15 | “=`` 16 | ”='' 17 | ‘=` 18 | ’=' 19 | —=--- 20 | 21 | [macros] 22 | (?su)(?[^\]]*?)\]=indexsee 23 | \(!html (?P(?:.|\n)*?)!\)=excluded 24 | \(!tex (?P(?:.|\n)*?)!\)=included 25 | \(!html (?P(?:.|\n)*?)!\)=excluded 26 | \(!book (?P(?:.|\n)*?)!\)=included 27 | (?su)(?[^(](?:[^()]|\n)*?)\)\)\)(?!\))=indexterm 28 | (?[^\s\(][^(](?:[^()]|\n)*?)\)\)(?!\))=indexterm2 29 | 30 | [tags] 31 | superscript=^!..braceleft..!|!..braceright..! 32 | subscript=_!..braceleft..!|!..braceright..! 33 | monospaced=!..backslash..!lstinline:::|::: 34 | 35 | [literal-inlinemacro] 36 | !..backslash..!lstinline:::{passtext}::: 37 | 38 | [monospacedwords] 39 | !..backslash..!lstinline:::{words}::: 40 | 41 | [quotes] 42 | `=#monospaced 43 | 44 | [link-inlinemacro] 45 | !..backslash..!hyperref[{target}]!..braceleft..!{0}!..braceright..! 46 | 47 | [indexsee-inlinemacro] 48 | !..backslash..!index!..braceleft..!{1}|see!..braceleft..!{2}!..braceright..!!..braceright..! 49 | 50 | [http-inlinemacro] 51 | !..backslash..!href!..braceleft..!http:{target}!..braceright..!!..braceleft..!{0}!..braceright..! 52 | 53 | [blockdef-listing] 54 | source-style=template="codeblock" 55 | 56 | [excluded-inlinemacro] 57 | {empty} 58 | 59 | [included-inlinemacro] 60 | {text} 61 | 62 | [image-blockmacro] 63 | \vskip 1.5ex 64 | \includegraphics[width={width=10cm}]\{{target}\} 65 | \vskip 1.5ex 66 | {newline} 67 | \noindent 68 | 69 | [partintroblock] 70 | template::[openblock] 71 | 72 | [openblock] 73 | | 74 | 75 | [quoteblock] 76 | {chapterquote?\epigraphhead[30]\{ } 77 | {chapterquote!\begin\{quote\}} 78 | {chapterquote?\epigraph\{\hspace*\{-.1cm\}\itshape``}|{chapterquote?''\}%} 79 | {attribution?\{---{attribution}{citetitle?, \emph\{{citetitle}\}}\}} 80 | {chapterquote?\}} 81 | {chapterquote!\end\{quote\}} 82 | {newline} 83 | 84 | [codeblock] 85 | \label\{{id}\} 86 | \begin\{lstlisting\}{newline} 87 | |{newline} 88 | \end\{lstlisting\} 89 | {newline} 90 | \noindent 91 | 92 | [listingblock] 93 | \begin\{lstlisting\}{newline} 94 | |{newline} 95 | \end\{lstlisting\} 96 | {newline} 97 | \noindent 98 | 99 | [paragraph] 100 | \label\{{id}\} 101 | | 102 | {newline} 103 | 104 | [header] 105 | \chapter\{{doctitle}\} 106 | {docid?\label\{{docid}\}} 107 | 108 | [sect1] 109 | \section\{{title}\} 110 | \label\{{id}\} 111 | {newline} 112 | | 113 | 114 | [sect2] 115 | \subsection\{{title}\} 116 | \label\{{id}\} 117 | {newline} 118 | | 119 | 120 | [sect3] 121 | \subsubsection\{{title}\} 122 | \label\{{id}\} 123 | {newline} 124 | | 125 | 126 | [footer] 127 | {empty} 128 | 129 | [table] 130 | \noindent\begin\{tabular\}\{ll\} 131 | {headrows} 132 | {bodyrows} 133 | {footrows} 134 | \end\{tabular\} 135 | {newline} 136 | \noindent 137 | -------------------------------------------------------------------------------- /bin/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/bin/.DS_Store -------------------------------------------------------------------------------- /bin/.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": {"node": {}} 3 | } 4 | -------------------------------------------------------------------------------- /bin/add_images_to_epub.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), path = require("path"); 2 | 3 | var images = []; 4 | function scanDir(dir) { 5 | fs.readdirSync(dir).forEach(function(file) { 6 | var full = path.resolve(dir, file), type; 7 | if (fs.lstatSync(full).isDirectory()) 8 | return scanDir(full); 9 | if (/\.svg$/.test(file)) 10 | type = "image/svg+xml"; 11 | else if (/\.png$/.test(file)) 12 | type = "image/png"; 13 | else if (/\.jpg$/.test(file)) 14 | type = "image/jpeg"; 15 | else 16 | throw new Error("Unknown image type: " + full); 17 | var local = full.slice(full.indexOf("/img/") + 1); 18 | images.push(" "); 19 | }); 20 | } 21 | scanDir("epub/img"); 22 | 23 | var out = fs.readFileSync("epub/content.opf.src", "utf8").replace("{{images}}", images.join("\n")); 24 | fs.writeFileSync("epub/content.opf", out); 25 | -------------------------------------------------------------------------------- /bin/addmarks: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var input = "" 4 | process.stdin.resume() 5 | process.stdin.on("data", function(chunk) { 6 | input += chunk.toString("utf8") 7 | }) 8 | 9 | process.stdin.on("end", function() { 10 | var re = /<(p|pre\ data-language|h[23])\b[^>]*>/g, m 11 | var pos = 0 12 | while (m = re.exec(input)) { 13 | var tag = m[1].match(/^\w+/)[0] 14 | var start = m.index + m[0].length 15 | var end = input.indexOf("", start) 16 | re.lastIndex = end + 3 + tag.length 17 | 18 | var type, toHash = toPlainText(input.slice(start, end)) 19 | if (tag == "pre") { 20 | type = "c" 21 | } else if (tag == "p") { 22 | type = "p" 23 | toHash = startAndEnd(toHash) 24 | } else { 25 | type = "h" 26 | } 27 | var mark = type + "_" + hash(toHash) 28 | var bookmark = "" 29 | process.stdout.write(input.slice(pos, start) + bookmark) 30 | pos = start 31 | } 32 | process.stdout.write(input.slice(pos)) 33 | }) 34 | 35 | function hash(string) { 36 | var sum = require("crypto").createHash("sha1"); 37 | sum.update(string); 38 | return sum.digest("base64").slice(0, 10); 39 | } 40 | 41 | function toPlainText(text) { 42 | return text.replace(/<[^>]+>|&[^;]+;/g, ""); 43 | } 44 | 45 | function startAndEnd(text) { 46 | var words = text.split(/\W+/); 47 | if (!words[0]) words.shift(); 48 | if (!words[words.length - 1]) words.pop(); 49 | if (words.length <= 6) return words.join(" "); 50 | return words.slice(0, 3).join(" ") + " " + words.slice(words.length - 3).join(" "); 51 | } 52 | -------------------------------------------------------------------------------- /bin/blockfilter: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var fs = require("fs"); 4 | 5 | var text = ""; 6 | 7 | var addBookmark = process.argv.indexOf("--bookmark") > -1; 8 | 9 | process.stdin.resume(); 10 | process.stdin.on("data", function(data) { 11 | text += data.toString("utf8"); 12 | }); 13 | process.stdin.on("end", function() { 14 | var pre = ""; 15 | if (addBookmark) { 16 | var mark = hash(toPlainText(text)); 17 | pre = ""; 18 | } 19 | var out = highlight(text, process.argv[process.argv.indexOf("-s") + 1].toLowerCase()) 20 | process.stdout.write(pre + out, "utf8"); 21 | }); 22 | 23 | function hash(string) { 24 | var sum = require("crypto").createHash("sha1"); 25 | sum.update(string); 26 | return sum.digest("base64").slice(0, 10); 27 | } 28 | 29 | function toPlainText(text) { 30 | return text.replace(/<[^>]+>|&[^;]+;/g, ""); 31 | } 32 | 33 | function startAndEnd(text) { 34 | var words = text.split(/\W+/); 35 | if (!words[0]) words.shift(); 36 | if (!words[words.length - 1]) words.pop(); 37 | if (words.length <= 6) return words.join(" "); 38 | return words.slice(0, 3).join(" ") + " " + words.slice(words.length - 3).join(" "); 39 | } 40 | 41 | CodeMirror = require("../node_modules/codemirror/addon/runmode/runmode.node.js"); 42 | require("../node_modules/codemirror/mode/meta.js"); 43 | 44 | function ensureMode(name) { 45 | if (CodeMirror.modes[name] || name == "null") return; 46 | try { 47 | require("../node_modules/codemirror/mode/" + name + "/" + name + ".js"); 48 | } catch(e) { 49 | console.error("Could not load mode " + name + "."); 50 | process.exit(1); 51 | } 52 | var obj = CodeMirror.modes[name]; 53 | if (obj.dependencies) obj.dependencies.forEach(ensureMode); 54 | } 55 | 56 | function esc(str) { 57 | return str.replace(/[<&]/g, function(ch) { return ch == "&" ? "&" : "<"; }); 58 | } 59 | 60 | function highlight(code, lang) { 61 | var modeName = lang; 62 | CodeMirror.modeInfo.forEach(function(info) { 63 | if (info.mime == lang) { 64 | modeName = info.mode; 65 | } else if (info.name.toLowerCase() == lang) { 66 | modeName = info.mode; 67 | lang = info.mime; 68 | } 69 | }); 70 | ensureMode(modeName); 71 | 72 | var curStyle = null, accum = "", output = ""; 73 | function flush() { 74 | if (curStyle) output += "" + esc(accum) + ""; 75 | else output += esc(accum); 76 | } 77 | 78 | CodeMirror.runMode(code, lang, function(text, style) { 79 | if (style != curStyle) { 80 | flush(); 81 | curStyle = style; accum = text; 82 | } else { 83 | accum += text; 84 | } 85 | }); 86 | flush(); 87 | return output; 88 | } 89 | -------------------------------------------------------------------------------- /bin/build_code.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | 3 | var file = process.argv[2]; 4 | var input = fs.readFileSync(file, "utf8"); 5 | 6 | var included = /\/\/ include_code(.*)\n(?:\/\/.*\n)*\s*(?:\[.*\n)*\[source,.*?\]\n----\n([\s\S]*?\n)----/g, m; 7 | var files = Object.create(null); 8 | var defaultFile = "code/chapter/" + file.replace(".txt", ".js"); 9 | 10 | while (m = included.exec(input)) { 11 | var snippet = m[2], directive = m[1], file = defaultFile; 12 | snippet = snippet.replace(/(\n|^)\s*\/\/ →.*\n/g, "$1"); 13 | if (directive.indexOf("strip_log") > -1) 14 | snippet = snippet.replace(/(\n|^)\s*console\.log\(.*\);\n/g, "$1"); 15 | if (m = directive.match(/top_lines:\s*(\d+)/)) 16 | snippet = snippet.split("\n").slice(0, Number(m[1])).join("\n") + "\n"; 17 | if (m = directive.match(/\s>(\S+)/)) 18 | file = m[1]; 19 | if (file in files) 20 | files[file].push(snippet); 21 | else 22 | files[file] = [snippet]; 23 | } 24 | 25 | for (var file in files) 26 | fs.writeFileSync(file, files[file].join("\n"), "utf8"); 27 | -------------------------------------------------------------------------------- /bin/check_links.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | 3 | var files = Object.create(null); 4 | fs.readdirSync(".").forEach(function(name) { 5 | var m = /^(\d\d.*)\.txt$/.exec(name); 6 | if (m) files[m[1]] = fs.readFileSync(name, "utf8"); 7 | }); 8 | files["22_fast"] = "[[fast]]"; // Kludge to recognize bonus chapter 9 | files["hints"] = "[[hints]]"; 10 | 11 | var fail = 0; 12 | function error(file, msg) { 13 | console.error(file + ": " + msg); 14 | fail = 1; 15 | } 16 | 17 | var link = /link:([^\.\s]+)\.(\w{2,5})#([^\s\[]+)\[/g, m; 18 | for (var file in files) { 19 | // Haha, good luck making sense of this one 20 | var bad = /\n\n(\(\(\([^()]*?(\([^()]+\))?\)\)\))*(\(\([^(]|[^(\n]\(\(\()|\n\n\[\[[^\]]+?\]\].|[^(]\(\([^()]+?(\([^()]+?\))?\)\)\)/.exec(files[file]); 21 | if (bad) error(file, "Suspicious notation: " + bad[0]); 22 | 23 | while (m = link.exec(files[file])) { 24 | if (m[2] != "html") 25 | error(file, "Bad extension: " + m[0]); 26 | var target = files[m[1]]; 27 | if (!target) 28 | error(file, "Unknown target file: " + m[0]); 29 | else if (m[3] != m[1].slice(3) && target.indexOf("[[" + m[3] + "]]") == -1) 30 | error(file, "Non-existing anchor: " + m[0]); 31 | } 32 | } 33 | 34 | process.exit(fail); 35 | -------------------------------------------------------------------------------- /bin/clean_latex.js: -------------------------------------------------------------------------------- 1 | var allowedUnicode = /[→←“”’π½⅓¼…×βϕ≈é—]/; 2 | function replaceUnicode(input) { 3 | var inCode = false; 4 | return input.replace(/\\(end|begin)\{lstlisting\}|([\x80-\uffff])/g, function(full, beginEnd, ch) { 5 | if (beginEnd) { 6 | inCode = beginEnd == "begin"; 7 | return full; 8 | } else { 9 | if (!allowedUnicode.test(ch)) { 10 | console.error("Found unhandled unicode: " + ch); 11 | process.exit(1); 12 | } 13 | if (inCode && /[“”]/.test(ch)) return '"'; 14 | if (inCode && /[‘’]/.test(ch)) return "'"; 15 | return ch; 16 | } 17 | }); 18 | } 19 | 20 | var escaped = {"\\{{}": "{", 21 | "\\}{}": "}", 22 | "\\textbackslash{}": "\\", 23 | "\\${}": "$", 24 | "\\textless{}": "<", 25 | "\\textgreater{}": ">", 26 | "\\&{}": "&", 27 | "\\_{}": "_", 28 | "\\%{}": "%", 29 | "\\#{}": "\\#", 30 | "\\textasciicircum{}": "^", 31 | "\\textasciitilde{}": "~", 32 | "\\textbar{}": "|", 33 | "\\textquotedbl{}": "\""}; 34 | var specials = Object.keys(escaped).map(function(k) {return k.replace(/[^\w\s]/g, "\\$&");}).join("|") + "|[^]"; 35 | var deEscapeRE = new RegExp("\\\\lstinline:::((?:" + specials + ")+?):::", "g"); 36 | var specialRE = new RegExp(specials, "g"); 37 | function cleanLstInline(str) { 38 | return str.replace(deEscapeRE, function(m, content) { 39 | return "\\lstinline`" + content.replace(specialRE, function(f) { 40 | if (f.length > 1) return escaped[f]; 41 | else if (f == "\n") return " "; 42 | else return f; 43 | }) + "`"; 44 | }); 45 | } 46 | 47 | var input = ""; 48 | process.stdin.on("data", function(chunk) { 49 | input += chunk; 50 | }); 51 | process.stdin.on("end", function() { 52 | input = input.replace(/(\n\n\\end{Code})|(\n{3,})|(_why,)|\\chapter\{(Introduction|Exercise Hints)\}|\\hyperref\[((?:[^\]]|\\_\{\})+)\]|\\index\{([^|}]+?)\\textbar\{\}see\{([^}]+)}}|\\textasciicircum\{\}\{([^\}]+?)\}|(���)/g, 53 | function(all, codeSpace, manyBlanks, why, simplechapter, link, seeFrom, seeTo, superscript, bogusChars) { 54 | if (codeSpace) 55 | return codeSpace.slice(1); 56 | if (manyBlanks) 57 | return "\n\n"; 58 | if (why) 59 | return "\\_why,"; 60 | if (simplechapter) 61 | return "\\chapter*{" + simplechapter + "}"; 62 | if (link) 63 | return "\\hyperref[" + link.replace(/\\_\{\}/g, "_") + "]"; 64 | if (seeFrom) 65 | return "\\index{" + seeFrom + "|see{" + seeTo + "}}"; 66 | if (superscript) 67 | return "\\textsuperscript{" + superscript + "}"; 68 | if (bogusChars) 69 | return "→"; 70 | }); 71 | input = cleanLstInline(input); 72 | input = input.replace(/({\\hspace\*\{.+?\}\\itshape``)\s*([^]+?)\s*('')/g, "$1$2$3"); 73 | input = replaceUnicode(input); 74 | 75 | process.stdout.write(input); 76 | }); 77 | process.stdin.resume(); 78 | -------------------------------------------------------------------------------- /bin/extract_hints.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"); 2 | 3 | process.stdout.write(":docid: hints\n\n= Exercise Hints =\n\nThe hints below might help when you are stuck with one of the exercises in this book. They don't give away the entire solution, but rather try to help you find it yourself.\n\n"); 4 | 5 | fs.readdirSync(".").forEach(function(name) { 6 | var m = /^(\d\d.*)\.txt$/.exec(name); 7 | if (!m) return; 8 | 9 | var file = fs.readFileSync(name, "utf8"); 10 | var title = file.match(/\n= (.*?) =\n/)[1], titleWritten = false; 11 | 12 | var curSubsection; 13 | var re = /\n=== (.*?) ===|!!hint!!([^]+?)!!hint!!/g; 14 | while (m = re.exec(file)) { 15 | if (m[1]) { 16 | curSubsection = m[1]; 17 | } else { 18 | if (!titleWritten) { 19 | process.stdout.write("== " + title + " ==\n\n"); 20 | titleWritten = true; 21 | } 22 | process.stdout.write("=== " + curSubsection + " ===\n\n" + m[2] + "\n"); 23 | } 24 | } 25 | }); 26 | -------------------------------------------------------------------------------- /bin/pre_epub.js: -------------------------------------------------------------------------------- 1 | // Script to pre-process the asciidoc sources for generating EPUB. 2 | 3 | var fs = require("fs"), child = require("child_process"); 4 | 5 | var infile = process.argv[2]; 6 | 7 | var instream; 8 | if (infile == "-") { 9 | instream = process.stdin; 10 | instream.resume(); 11 | } else { 12 | instream = fs.createReadStream(infile); 13 | } 14 | 15 | var input = ""; 16 | instream.on("data", function(chunk) { 17 | input += chunk; 18 | }); 19 | instream.on("end", function() { 20 | if (infile != "-") 21 | input = ":docid: " + infile.match(/^\d{2}_(.*?)\.txt/)[1] + "\n" + input; 22 | process.stdout.write(input.replace(/\blink:([^\.]+)\.html#(.*?)\[|!!(hint)!![^]+?!!hint!!(?:\n|$)/g, 23 | function(match, linkFile, linkID, hint) { 24 | if (linkFile) { 25 | return "link:" + linkFile + ".xhtml#" + linkID + "["; 26 | } else if (hint) { 27 | return ""; 28 | } 29 | })); 30 | }); 31 | -------------------------------------------------------------------------------- /bin/pre_latex.js: -------------------------------------------------------------------------------- 1 | // Script to pre-process the asciidoc sources for generating LaTeX. 2 | // Adjusts some stylistic things to satisfy No Starch's style, and 3 | // fixes up svg images to point at pdf versions of the images. 4 | 5 | var fs = require("fs"), child = require("child_process"); 6 | 7 | var infile, nostarch = false; 8 | for (var i = 2; i < process.argv.length; i++) { 9 | var arg = process.argv[i]; 10 | if (arg == "--nostarch") nostarch = true; 11 | else infile = arg; 12 | } 13 | 14 | var instream; 15 | if (infile == "-") { 16 | instream = process.stdin; 17 | instream.resume(); 18 | } else { 19 | instream = fs.createReadStream(infile); 20 | } 21 | 22 | var titleCaseSmallWords = "a an the at by for in of on to up and as but with or nor if console.log".split(" "); 23 | 24 | var input = ""; 25 | instream.on("data", function(chunk) { 26 | input += chunk; 27 | }); 28 | instream.on("end", function() { 29 | if (infile != "-") 30 | input = ":docid: " + infile.match(/^\d{2}_(.*?)\.txt/)[1] + "\n" + input; 31 | process.stdout.write(input.replace(/\n===? (.*?) ===?|”([.,:;])|\nimage::img\/(.+?)\.(\w+)(\[.*\])?|link:[^\.]+\.html#(.*?)\[|!!(hint)!![^]+?!!hint!!(?:\n|$)/g, 32 | function(match, title, quoted, imgName, imgType, imgOpts, link, solution) { 33 | if (title) { // Section title, must be converted to title case 34 | if (!nostarch) return match; 35 | var kind = /^\n(=*)/.exec(match)[1]; 36 | return "\n" + kind + " " + title.split(" ").map(function(word) { 37 | if (titleCaseSmallWords.indexOf(word) == -1) 38 | return word[0].toUpperCase() + word.slice(1); 39 | else 40 | return word; 41 | }).join(" ") + " " + kind; 42 | } else if (quoted) { // Move punctuation into quotes 43 | if (!nostarch) return match; 44 | return quoted + "”"; 45 | } else if (imgName) { // Image file 46 | return "\nimage::" + convertImage(imgName, imgType) + (!imgOpts ? "" : nostarch ? imgOpts : calcWidth(imgOpts)); 47 | } else if (link) { 48 | return "link:" + link + "["; 49 | } else if (solution) { 50 | return ""; 51 | } 52 | })); 53 | }); 54 | 55 | function calcWidth(opts) { 56 | var replaced = false; 57 | opts = opts.replace(/width=\"([\d.]+)cm\"/, function(m, w) { 58 | replaced = true; 59 | return "width=\"" + (Number(w)/11).toFixed(2) + "\\\\textwidth\""; 60 | }); 61 | if (!replaced) return opts.slice(0, opts.length - 1) + ",width=\"0.95\\\\textwidth\"]"; 62 | else return opts; 63 | } 64 | 65 | function convertImage(name, type) { 66 | var oldName = "img/" + name + "." + type; 67 | if (type == "svg") { 68 | var newName = "img/generated/" + name + ".pdf"; 69 | try { 70 | var newAge = fs.statSync(newName).atime; 71 | } catch (e) { 72 | newAge = 0; 73 | } 74 | return newName; 75 | } 76 | return oldName; 77 | } 78 | -------------------------------------------------------------------------------- /bin/wrap.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \usepackage{ifthen} 3 | \newboolean{DBKIsBook} 4 | \setboolean{DBKIsBook}{false} 5 | \IfFileExists{ifxetex.sty}{% 6 | \usepackage{ifxetex}% 7 | }{% 8 | \newif\ifxetex 9 | \xetexfalse 10 | } 11 | \ifxetex 12 | \usepackage{fontspec} 13 | \usepackage{xltxtra} 14 | \defaultfontfeatures{Mapping=tex-text} 15 | \setmainfont{DejaVu Serif} 16 | \setsansfont{DejaVu Sans} 17 | \setmonofont{DejaVu Sans Mono} 18 | \else 19 | \usepackage[T1]{fontenc} 20 | \usepackage[latin1]{inputenc} 21 | \fi 22 | \usepackage{fancybox} 23 | \usepackage{makeidx} 24 | \usepackage[hyperlink]{docbook} 25 | \renewcommand{\DBKreleaseinfo}{} 26 | \renewcommand{\DBKrevhistory}{} 27 | \setcounter{tocdepth}{5} 28 | \setcounter{secnumdepth}{5} 29 | \renewcommand{\DBKdate}{2014-{}06-{}23} 30 | \title{Eloquent JavaScript} 31 | \author{Marijn Haverbeke} 32 | \renewcommand{\DBKindexation}{} 33 | \makeindex 34 | \makeglossary 35 | \begin{document} 36 | \lstsetup 37 | \maketitle 38 | \tableofcontents 39 | \mainmatter 40 | 41 | {{CHAPTERS}} 42 | 43 | \end{document} 44 | \printindex 45 | -------------------------------------------------------------------------------- /code/animateworld.js: -------------------------------------------------------------------------------- 1 | // test: no 2 | 3 | (function() { 4 | "use strict"; 5 | 6 | var active = null; 7 | 8 | function Animated(world) { 9 | this.world = world; 10 | var outer = (window.__sandbox ? window.__sandbox.output.div : document.body), doc = outer.ownerDocument; 11 | var node = outer.appendChild(doc.createElement("div")); 12 | node.style.cssText = "position: relative; width: intrinsic; width: fit-content;"; 13 | this.pre = node.appendChild(doc.createElement("pre")); 14 | this.pre.appendChild(doc.createTextNode(world.toString())); 15 | this.button = node.appendChild(doc.createElement("div")); 16 | this.button.style.cssText = "position: absolute; bottom: 8px; right: -4.5em; color: white; font-family: tahoma, arial; " + 17 | "background: #4ab; cursor: pointer; border-radius: 18px; font-size: 70%; width: 3.5em; text-align: center;"; 18 | this.button.innerHTML = "stop"; 19 | var self = this; 20 | this.button.addEventListener("click", function() { self.clicked(); }); 21 | this.disabled = false; 22 | if (active) active.disable(); 23 | active = this; 24 | this.interval = setInterval(function() { self.tick(); }, 333); 25 | } 26 | 27 | Animated.prototype.clicked = function() { 28 | if (this.disabled) return; 29 | if (this.interval) { 30 | clearInterval(this.interval); 31 | this.interval = null; 32 | this.button.innerHTML = "start"; 33 | } else { 34 | var self = this; 35 | this.interval = setInterval(function() { self.tick(); }, 333); 36 | this.button.innerHTML = "stop"; 37 | } 38 | }; 39 | 40 | Animated.prototype.tick = function() { 41 | this.world.turn(); 42 | this.pre.removeChild(this.pre.firstChild); 43 | this.pre.appendChild(this.pre.ownerDocument.createTextNode(this.world.toString())); 44 | }; 45 | 46 | Animated.prototype.disable = function() { 47 | this.disabled = true; 48 | clearInterval(this.interval); 49 | this.button.innerHTML = "Disabled"; 50 | this.button.style.color = "red"; 51 | }; 52 | 53 | window.animateWorld = function(world) { new Animated(world); }; 54 | })(); 55 | -------------------------------------------------------------------------------- /code/chapter/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/code/chapter/.keep -------------------------------------------------------------------------------- /code/draw_graph.js: -------------------------------------------------------------------------------- 1 | // The familiar Vector type. 2 | 3 | function Vector(x, y) { this.x = x; this.y = y; } 4 | 5 | Vector.prototype.plus = function(other) { 6 | return new Vector(this.x + other.x, this.y + other.y); 7 | }; 8 | Vector.prototype.minus = function(other) { 9 | return new Vector(this.x - other.x, this.y - other.y); 10 | }; 11 | Vector.prototype.times = function(factor) { 12 | return new Vector(this.x * factor, this.y * factor); 13 | }; 14 | Object.defineProperty(Vector.prototype, "length", { 15 | get: function() { 16 | return Math.sqrt(this.x * this.x + this.y * this.y); 17 | } 18 | }); 19 | 20 | // Since we will want to inspect the layouts our code produces, let's 21 | // first write code to draw a graph onto a canvas. Since we don't know 22 | // in advance how big the graph is, the `Scale` object computes a 23 | // scale and offset so that all nodes fit onto the given canvas. 24 | 25 | var nodeSize = 8; 26 | 27 | function drawGraph(graph) { 28 | var canvas = document.querySelector("canvas"); 29 | if (!canvas) { 30 | canvas = document.body.appendChild(document.createElement("canvas")); 31 | canvas.width = canvas.height = 500; 32 | } 33 | var cx = canvas.getContext("2d"); 34 | 35 | cx.clearRect(0, 0, canvas.width, canvas.height); 36 | var scale = new Scale(graph, canvas.width, canvas.height); 37 | 38 | // Draw the edges. 39 | cx.strokeStyle = "orange"; 40 | cx.lineWidth = 3; 41 | graph.forEach(function(origin, i) { 42 | origin.edges.forEach(function(target) { 43 | if (graph.indexOf(target) <= i) return; 44 | cx.beginPath(); 45 | cx.moveTo(scale.x(origin.pos.x), scale.y(origin.pos.y)); 46 | cx.lineTo(scale.x(target.pos.x), scale.y(target.pos.y)); 47 | cx.stroke(); 48 | }); 49 | }); 50 | 51 | // Draw the nodes. 52 | cx.fillStyle = "purple"; 53 | graph.forEach(function(node) { 54 | cx.beginPath(); 55 | cx.arc(scale.x(node.pos.x), scale.y(node.pos.y), nodeSize, 0, 7); 56 | cx.fill(); 57 | }); 58 | } 59 | 60 | // The function starts by drawing the edges, so that they appear 61 | // behind the nodes. Since the nodes on _both_ side of an edge refer 62 | // to each other, and we don't want to draw every edge twice, edges 63 | // are only drawn then the target comes _after_ the current node in 64 | // the `graph` array. 65 | 66 | // When the edges have been drawn, the nodes are drawn on top of them 67 | // as purple discs. Remember that the last argument to `arc` gives the 68 | // rotation, and we have to pass something bigger than 2π to get a 69 | // full circle. 70 | 71 | // Finding a scale at which to draw the graph is done by finding the 72 | // top left and bottom right corners of the area taken up by the 73 | // nodes. The offset at which nodes are drawn is based on the top left 74 | // corner, and the scale is based on the size of the canvas divided by 75 | // the distance between those corners. The function reserves space 76 | // along the sides of the canvas based on the `nodeSize` variable, so 77 | // that the circles drawn around nodes’ center points don't get cut off. 78 | 79 | function Scale(graph, width, height) { 80 | var xs = graph.map(function(node) { return node.pos.x }); 81 | var ys = graph.map(function(node) { return node.pos.y }); 82 | var minX = Math.min.apply(null, xs); 83 | var minY = Math.min.apply(null, ys); 84 | var maxX = Math.max.apply(null, xs); 85 | var maxY = Math.max.apply(null, ys); 86 | 87 | this.offsetX = minX; this.offsetY = minY; 88 | this.scaleX = (width - 2 * nodeSize) / (maxX - minX); 89 | this.scaleY = (height - 2 * nodeSize) / (maxY - minY); 90 | } 91 | 92 | Scale.prototype.x = function(x) { 93 | return this.scaleX * (x - this.offsetX) + nodeSize; 94 | }; 95 | Scale.prototype.y = function(y) { 96 | return this.scaleY * (y - this.offsetY) + nodeSize; 97 | }; 98 | 99 | // The `x` and `y` methods of the `Scale` type convert from graph 100 | // coordinates into canvas coordinates. 101 | -------------------------------------------------------------------------------- /code/file_server_promises.js: -------------------------------------------------------------------------------- 1 | var http = require("http"), fs = require("fs"); 2 | var Promise = require("promise"); 3 | 4 | var methods = Object.create(null); 5 | 6 | // Remember that promises can either fail or succeed. The `then` 7 | // method takes two callbacks, one to handle success and one to handle 8 | // failure. The strategy for dealing with exceptions and other failure 9 | // is to notice them in the second callback passed here, and return a 10 | // 500 response. 11 | // 12 | // On success, the promise returned by respondTo should return an 13 | // object with a `code` property indicating the response code, and 14 | // optional `body` and `type` properties. The body can be a stream to 15 | // directly pipe into the response, or a string. 16 | 17 | http.createServer(function(request, response) { 18 | respondTo(request).then(function(data) { 19 | response.writeHead(data.code, {"Content-Type": data.type || "text/plain"}); 20 | if (data.body && data.body.pipe) 21 | data.body.pipe(response); 22 | else 23 | response.end(data.body); 24 | }, function(error) { 25 | response.writeHead(500); 26 | response.end(error.toString()); 27 | console.log("Response failed: ", error.stack); 28 | }); 29 | }).listen(8000); 30 | 31 | function respondTo(request) { 32 | if (request.method in methods) 33 | return methods[request.method](urlToPath(request.url), request); 34 | else 35 | return Promise.resolve({code: 405, 36 | body: "Method " + request.method + " not allowed."}); 37 | } 38 | 39 | function urlToPath(url) { 40 | var path = require("url").parse(url).pathname; 41 | var decoded = decodeURIComponent(path); 42 | return "." + decoded.replace(/(\/|\\)\.\.(\/|\\|$)/g, "/"); 43 | } 44 | 45 | // Wrap the fs functions that we need with Promise.denodeify, so that 46 | // they return promises instead of directly taking a callback and 47 | // passing it an error argument. 48 | 49 | var fsp = {}; 50 | ["stat", "readdir", "rmdir", "unlink", "mkdir"].forEach(function(method) { 51 | fsp[method] = Promise.denodeify(fs[method]); 52 | }); 53 | 54 | // Since several functions need to call `fsp.stat` and handle failures 55 | // that indicate non-existent files in a special way, this is a 56 | // convenience wrapper that converts file-not-found failures into 57 | // success with a null value. 58 | // 59 | // Remember that calling the `then` method returns *another* promise, 60 | // and that having a failure handler return normally replaces the 61 | // failure a success (using the returned value). We're passing null 62 | // for the success handler here (letting through normall successes 63 | // unchanged), and changing one kind of failure into success. 64 | 65 | function inspectPath(path) { 66 | return fsp.stat(path).then(null, function(error) { 67 | if (error.code == "ENOENT") return null; 68 | else throw error; 69 | }); 70 | } 71 | 72 | // We can get by with much less explicit error handling, now that 73 | // failures automatically propagate back. The new promise returned by 74 | // `then`, as returned from this function, will use one of the values 75 | // returned here (objects with `code` properties) as its value. When a 76 | // handler passed to `then` returns another promise (as in the case 77 | // when the path refers to a directory), that promise will be 78 | // connected the promise returned by `then`, determining when and how 79 | // it is resolved. 80 | 81 | methods.GET = function(path) { 82 | return inspectPath(path).then(function(stats) { 83 | if (!stats) // Does not exist 84 | return {code: 404, body: "File not found"}; 85 | else if (stats.isDirectory()) 86 | return fsp.readdir(path).then(function(files) { 87 | return {code: 200, body: files.join("\n")}; 88 | }); 89 | else 90 | return {code: 200, 91 | type: require("mime").lookup(path), 92 | body: fs.createReadStream(path)}; 93 | }); 94 | }; 95 | 96 | var noContent = {code: 204}; 97 | function returnNoContent() { return noContent; } 98 | 99 | // Though failure is propagated automatically, we still have to 100 | // arrange for `noContent` to be returned when an action finishes, 101 | // which is the role of `returnNoContent` success handler. 102 | 103 | methods.DELETE = function(path) { 104 | return inspectPath(path).then(function(stats) { 105 | if (!stats) 106 | return noContent; 107 | else if (stats.isDirectory()) 108 | return fsp.rmdir(path).then(returnNoContent); 109 | else 110 | return fsp.unlink(path).then(returnNoContent); 111 | }); 112 | }; 113 | 114 | // To wrap a stream, we have to define our own promise, since 115 | // Promise.denodeify can only wrap simple functions. 116 | 117 | methods.PUT = function(path, request) { 118 | return new Promise(function(success, failure) { 119 | var outStream = fs.createWriteStream(path); 120 | outStream.on("error", failure); 121 | outStream.on("finish", success.bind(null, noContent)); 122 | request.pipe(outStream); 123 | }); 124 | }; 125 | 126 | methods.MKCOL = function(path, request) { 127 | return inspectPath(path).then(function(stats) { 128 | if (!stats) 129 | return fsp.mkdir(path).then(returnNoContent); 130 | if (stats.isDirectory()) 131 | return noContent; 132 | else 133 | return {code: 400, body: "File exists"}; 134 | }); 135 | }; 136 | -------------------------------------------------------------------------------- /code/hello.js: -------------------------------------------------------------------------------- 1 | alert("hello!"); 2 | -------------------------------------------------------------------------------- /code/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Eloquent JavaScript :: Code Sandbox 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 |
20 |

Code Sandbox

21 | 22 |

You can use this page to download source code and solutions to 23 | exercises for the book Eloquent JavaScript, and to directly run code 24 | in the context of chapters from that book, either to solve exercises 25 | to simply play around.

26 | 27 |

28 | Chapter: 29 | 30 | 31 |

32 | 33 |
34 | 35 |
36 |
37 | 38 |
39 | To run this chapter's code locally, use these files: 40 |
    41 |
    42 | 43 |
    44 | These files contain this chapter’s project code: 45 |
      46 |
      47 | 48 |

      If you've solved the exercise and want to compare your code with 49 | mine, or you really tried, but can't get your code to work, 50 | you can (or download it).

      52 | 53 |

      54 | The base environment for this chapter (if any) is available in the 55 | sandbox above, allowing you to run the chapter's examples by 56 | simply pasting them into the editor. 57 |

      58 |
      59 | -------------------------------------------------------------------------------- /code/intro.js: -------------------------------------------------------------------------------- 1 | function range(start, end, step) { 2 | if (step == null) step = 1; 3 | var array = []; 4 | 5 | if (step > 0) { 6 | for (var i = start; i <= end; i += step) 7 | array.push(i); 8 | } else { 9 | for (var i = start; i >= end; i += step) 10 | array.push(i); 11 | } 12 | return array; 13 | } 14 | 15 | function sum(array) { 16 | var total = 0; 17 | for (var i = 0; i < array.length; i++) 18 | total += array[i]; 19 | return total; 20 | } 21 | -------------------------------------------------------------------------------- /code/load.js: -------------------------------------------------------------------------------- 1 | // Since the code for most chapter in Eloquent JavaScript isn't 2 | // written with node's module system in mind, this kludge is used to 3 | // load dependency files into the global namespace, so that the 4 | // examples can run on node. 5 | 6 | module.exports = function() { 7 | for (var i = 0; i < arguments.length; i++) 8 | (1,eval)(require("fs").readFileSync(__dirname + "/../" + arguments[i], "utf8")); 9 | }; 10 | -------------------------------------------------------------------------------- /code/loadfile.js: -------------------------------------------------------------------------------- 1 | function readFile(name) { 2 | return readFile.files[name] || ""; 3 | } 4 | readFile.files = { 5 | "weekDay": 'var names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];\ 6 | exports.name = function(number) { return names[number]; };\ 7 | exports.number = function(name) { return names.indexOf(name); };', 8 | "today": 'exports.dayNumber = function() { return (new Date).getDay(); };' 9 | }; 10 | 11 | function backgroundReadFile(name, c) { 12 | setTimeout(function() { 13 | c(backgroundReadFile.files[name] || ""); 14 | }, 200 * Math.random()); 15 | } 16 | backgroundReadFile.files = { 17 | "weekDay": 'define([], function() {\ 18 | var names = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];\ 19 | return { name: function(number) { return names[number]; }, number: function(name) { return names.indexOf(name); }};\ 20 | });', 21 | "today": 'define([], function() { return {dayNumber: function() { return (new Date).getDay(); }}; });' 22 | }; 23 | 24 | var exports = {}; 25 | -------------------------------------------------------------------------------- /code/mountains.js: -------------------------------------------------------------------------------- 1 | var MOUNTAINS = [ 2 | {name: "Kilimanjaro", height: 5895, country: "Tanzania"}, 3 | {name: "Everest", height: 8848, country: "Nepal"}, 4 | {name: "Mount Fuji", height: 3776, country: "Japan"}, 5 | {name: "Mont Blanc", height: 4808, country: "Italy/France"}, 6 | {name: "Vaalserberg", height: 323, country: "Netherlands"}, 7 | {name: "Denali", height: 6168, country: "United States"}, 8 | {name: "Popocatepetl", height: 5465, country: "Mexico"} 9 | ]; 10 | 11 | if (typeof module != "undefined" && module.exports) 12 | module.exports = MOUNTAINS; 13 | -------------------------------------------------------------------------------- /code/promise.js: -------------------------------------------------------------------------------- 1 | !function(){var a,b,c,d;!function(){var e={},f={};a=function(a,b,c){e[a]={deps:b,callback:c}},d=c=b=function(a){function c(b){if("."!==b.charAt(0))return b;for(var c=b.split("/"),d=a.split("/").slice(0,-1),e=0,f=c.length;f>e;e++){var g=c[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(d._eak_seen=e,f[a])return f[a];if(f[a]={},!e[a])throw new Error("Could not find module "+a);for(var g,h=e[a],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)"exports"===i[l]?k.push(g={}):k.push(b(c(i[l])));var n=j.apply(this,k);return f[a]=g||n}}(),a("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j=0.5.0"}, 7 | "licenses": [{"type": "MIT", 8 | "url": "http://opensource.org/licenses/MIT"}], 9 | "bugs": "http://github.com/marijnh/Eloquent-JavaScript/issues", 10 | "homepage": "http://eloquentjavascript.net/21_skillsharing.html", 11 | "maintainers":[{"name": "Marijn Haverbeke", 12 | "email": "marijnh@gmail.com", 13 | "web": "http://marijnhaverbeke.nl"}], 14 | "repository": {"type": "git", 15 | "url": "https://github.com/marijnh/Eloquent-JavaScript.git"} 16 | } 17 | -------------------------------------------------------------------------------- /code/skillsharing/public/skillsharing.css: -------------------------------------------------------------------------------- 1 | #talks { 2 | margin-bottom: 25px; 3 | } 4 | 5 | .talk { 6 | margin-bottom: 5px; 7 | padding: 8px; 8 | border: 1px solid silver; 9 | } 10 | 11 | .name { 12 | font-weight: bold; 13 | } 14 | 15 | .comment { 16 | font-style: italic; 17 | } 18 | 19 | .talk h2 { 20 | margin: 0; 21 | font-size: 130%; 22 | } 23 | -------------------------------------------------------------------------------- /code/solutions/02_1_iterando_un_tringulo.js: -------------------------------------------------------------------------------- 1 | for (var line = "#"; line.length < 8; line += "#") 2 | console.log(line); 3 | -------------------------------------------------------------------------------- /code/solutions/02_2_fizzbuzz.js: -------------------------------------------------------------------------------- 1 | for (var n = 1; n <= 100; n++) { 2 | var output = ""; 3 | if (n % 3 == 0) 4 | output += "Fizz"; 5 | if (n % 5 == 0) 6 | output += "Buzz"; 7 | console.log(output || n); 8 | } 9 | -------------------------------------------------------------------------------- /code/solutions/02_3_tablero_de_ajedrez.js: -------------------------------------------------------------------------------- 1 | var size = 8; 2 | 3 | var board = ""; 4 | 5 | for (var y = 0; y < size; y++) { 6 | for (var x = 0; x < size; x++) { 7 | if ((x + y) % 2 == 0) 8 | board += " "; 9 | else 10 | board += "#"; 11 | } 12 | board += "\n"; 13 | } 14 | 15 | console.log(board); 16 | -------------------------------------------------------------------------------- /code/solutions/03_1_mnimo.js: -------------------------------------------------------------------------------- 1 | function min(a, b) { 2 | if (a < b) 3 | return a; 4 | else 5 | return b; 6 | } 7 | 8 | console.log(min(0, 10)); 9 | // → 0 10 | console.log(min(0, -10)); 11 | // → -10 12 | -------------------------------------------------------------------------------- /code/solutions/03_2_recursin.js: -------------------------------------------------------------------------------- 1 | function isEven(n) { 2 | if (n == 0) 3 | return true; 4 | else if (n == 1) 5 | return false; 6 | else if (n < 0) 7 | return isEven(-n); 8 | else 9 | return isEven(n - 2); 10 | } 11 | 12 | 13 | console.log(isEven(50)); 14 | // → true 15 | console.log(isEven(75)); 16 | // → false 17 | console.log(isEven(-1)); 18 | // → false 19 | -------------------------------------------------------------------------------- /code/solutions/03_3_contando_frijoles.js: -------------------------------------------------------------------------------- 1 | function countChar(string, ch) { 2 | var counted = 0; 3 | for (var i = 0; i < string.length; i++) 4 | if (string.charAt(i) == ch) 5 | counted += 1; 6 | return counted; 7 | } 8 | 9 | function countBs(string) { 10 | return countChar(string, "B"); 11 | } 12 | 13 | console.log(countBs("BBC")); 14 | // → 2 15 | console.log(countChar("kakkerlak", "k")); 16 | // → 4 17 | -------------------------------------------------------------------------------- /code/solutions/04_1_the_sum_of_a_range.js: -------------------------------------------------------------------------------- 1 | function range(start, end, step) { 2 | if (step == null) step = 1; 3 | var array = []; 4 | 5 | if (step > 0) { 6 | for (var i = start; i <= end; i += step) 7 | array.push(i); 8 | } else { 9 | for (var i = start; i >= end; i += step) 10 | array.push(i); 11 | } 12 | return array; 13 | } 14 | 15 | function sum(array) { 16 | var total = 0; 17 | for (var i = 0; i < array.length; i++) 18 | total += array[i]; 19 | return total; 20 | } 21 | 22 | console.log(range(1, 10)) 23 | // → [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 24 | console.log(range(5, 2, -1)); 25 | // → [5, 4, 3, 2] 26 | console.log(sum(range(1, 10))); 27 | // → 55 28 | -------------------------------------------------------------------------------- /code/solutions/04_2_reversing_an_array.js: -------------------------------------------------------------------------------- 1 | function reverseArray(array) { 2 | var output = []; 3 | for (var i = array.length - 1; i >= 0; i--) 4 | output.push(array[i]); 5 | return output; 6 | } 7 | 8 | function reverseArrayInPlace(array) { 9 | for (var i = 0; i < Math.floor(array.length / 2); i++) { 10 | var old = array[i]; 11 | array[i] = array[array.length - 1 - i]; 12 | array[array.length - 1 - i] = old; 13 | } 14 | return array; 15 | } 16 | 17 | console.log(reverseArray(["A", "B", "C"])); 18 | // → ["C", "B", "A"]; 19 | var arrayValue = [1, 2, 3, 4, 5]; 20 | reverseArrayInPlace(arrayValue); 21 | console.log(arrayValue); 22 | // → [5, 4, 3, 2, 1] 23 | -------------------------------------------------------------------------------- /code/solutions/04_3_a_list.js: -------------------------------------------------------------------------------- 1 | function arrayToList(array) { 2 | var list = null; 3 | for (var i = array.length - 1; i >= 0; i--) 4 | list = {value: array[i], rest: list}; 5 | return list; 6 | } 7 | 8 | function listToArray(list) { 9 | var array = []; 10 | for (var node = list; node; node = node.rest) 11 | array.push(node.value); 12 | return array; 13 | } 14 | 15 | function prepend(value, list) { 16 | return {value: value, rest: list}; 17 | } 18 | 19 | function nth(list, n) { 20 | if (!list) 21 | return undefined; 22 | else if (n == 0) 23 | return list.value; 24 | else 25 | return nth(list.rest, n - 1); 26 | } 27 | 28 | console.log(arrayToList([10, 20])); 29 | // → {value: 10, rest: {value: 20, rest: null}} 30 | console.log(listToArray(arrayToList([10, 20, 30]))); 31 | // → [10, 20, 30] 32 | console.log(prepend(10, prepend(20, null))); 33 | // → {value: 10, rest: {value: 20, rest: null}} 34 | console.log(nth(arrayToList([10, 20, 30]), 1)); 35 | // → 20 36 | -------------------------------------------------------------------------------- /code/solutions/04_4_deep_comparison.js: -------------------------------------------------------------------------------- 1 | function deepEqual(a, b) { 2 | if (a === b) return true; 3 | 4 | if (a == null || typeof a != "object" || 5 | b == null || typeof b != "object") 6 | return false; 7 | 8 | var propsInA = 0, propsInB = 0; 9 | 10 | for (var prop in a) 11 | propsInA += 1; 12 | 13 | for (var prop in b) { 14 | propsInB += 1; 15 | if (!(prop in a) || !deepEqual(a[prop], b[prop])) 16 | return false; 17 | } 18 | 19 | return propsInA == propsInB; 20 | } 21 | 22 | var obj = {here: {is: "an"}, object: 2}; 23 | console.log(deepEqual(obj, obj)); 24 | // → true 25 | console.log(deepEqual(obj, {here: 1, object: 2})); 26 | // → false 27 | console.log(deepEqual(obj, {here: {is: "an"}, object: 2})); 28 | // → true 29 | -------------------------------------------------------------------------------- /code/solutions/05_1_flattening.js: -------------------------------------------------------------------------------- 1 | var arrays = [[1, 2, 3], [4, 5], [6]]; 2 | 3 | console.log(arrays.reduce(function(flat, current) { 4 | return flat.concat(current); 5 | }, [])); 6 | 7 | // → [1, 2, 3, 4, 5, 6] 8 | -------------------------------------------------------------------------------- /code/solutions/05_2_mother-child_age_difference.js: -------------------------------------------------------------------------------- 1 | function average(array) { 2 | function plus(a, b) { return a + b; } 3 | return array.reduce(plus) / array.length; 4 | } 5 | 6 | var byName = {}; 7 | ancestry.forEach(function(person) { 8 | byName[person.name] = person; 9 | }); 10 | 11 | var differences = ancestry.filter(function(person) { 12 | return byName[person.mother] != null; 13 | }).map(function(person) { 14 | return person.born - byName[person.mother].born; 15 | }); 16 | 17 | console.log(average(differences)); 18 | // → 31.2 19 | -------------------------------------------------------------------------------- /code/solutions/05_3_historical_life_expectancy.js: -------------------------------------------------------------------------------- 1 | function average(array) { 2 | function plus(a, b) { return a + b; } 3 | return array.reduce(plus) / array.length; 4 | } 5 | 6 | function groupBy(array, groupOf) { 7 | var groups = {}; 8 | array.forEach(function(element) { 9 | var groupName = groupOf(element); 10 | if (groupName in groups) 11 | groups[groupName].push(element); 12 | else 13 | groups[groupName] = [element]; 14 | }); 15 | return groups; 16 | } 17 | 18 | var byCentury = groupBy(ancestry, function(person) { 19 | return Math.ceil(person.died / 100); 20 | }); 21 | 22 | for (var century in byCentury) { 23 | var ages = byCentury[century].map(function(person) { 24 | return person.died - person.born; 25 | }); 26 | console.log(century + ": " + average(ages)); 27 | } 28 | 29 | // → 16: 43.5 30 | // 17: 51.2 31 | // 18: 52.8 32 | // 19: 54.8 33 | // 20: 84.7 34 | // 21: 94 35 | -------------------------------------------------------------------------------- /code/solutions/05_4_every_and_then_some.js: -------------------------------------------------------------------------------- 1 | function every(array, predicate) { 2 | for (var i = 0; i < array.length; i++) { 3 | if (!predicate(array[i])) 4 | return false; 5 | } 6 | return true; 7 | } 8 | 9 | function some(array, predicate) { 10 | for (var i = 0; i < array.length; i++) { 11 | if (predicate(array[i])) 12 | return true; 13 | } 14 | return false; 15 | } 16 | 17 | console.log(every([NaN, NaN, NaN], isNaN)); 18 | // → true 19 | console.log(every([NaN, NaN, 4], isNaN)); 20 | // → false 21 | console.log(some([NaN, 3, 4], isNaN)); 22 | // → true 23 | console.log(some([2, 3, 4], isNaN)); 24 | // → false 25 | -------------------------------------------------------------------------------- /code/solutions/06_1_un_tipo_vector.js: -------------------------------------------------------------------------------- 1 | function Vector(x, y) { 2 | this.x = x; 3 | this.y = y; 4 | } 5 | 6 | Vector.prototype.plus = function(other) { 7 | return new Vector(this.x + other.x, this.y + other.y); 8 | }; 9 | 10 | Vector.prototype.minus = function(other) { 11 | return new Vector(this.x - other.x, this.y - other.y); 12 | }; 13 | 14 | Object.defineProperty(Vector.prototype, "length", { 15 | get: function() { 16 | return Math.sqrt(this.x * this.x + this.y * this.y); 17 | } 18 | }); 19 | 20 | console.log(new Vector(1, 2).plus(new Vector(2, 3))); 21 | // → Vector{x: 3, y: 5} 22 | console.log(new Vector(1, 2).minus(new Vector(2, 3))); 23 | // → Vector{x: -1, y: -1} 24 | console.log(new Vector(3, 4).length); 25 | // → 5 26 | -------------------------------------------------------------------------------- /code/solutions/06_2_otra_celda.js: -------------------------------------------------------------------------------- 1 | function StretchCell(inner, width, height) { 2 | this.inner = inner; 3 | this.width = width; 4 | this.height = height; 5 | } 6 | 7 | StretchCell.prototype.minWidth = function() { 8 | return Math.max(this.width, this.inner.minWidth()); 9 | }; 10 | StretchCell.prototype.minHeight = function() { 11 | return Math.max(this.height, this.inner.minHeight()); 12 | }; 13 | StretchCell.prototype.draw = function(width, height) { 14 | return this.inner.draw(width, height); 15 | }; 16 | 17 | var sc = new StretchCell(new TextCell("abc"), 1, 2); 18 | console.log(sc.minWidth()); 19 | // → 3 20 | console.log(sc.minHeight()); 21 | // → 2 22 | console.log(sc.draw(3, 2)); 23 | // → ["abc", " "] 24 | -------------------------------------------------------------------------------- /code/solutions/06_3_interface_secuencia.js: -------------------------------------------------------------------------------- 1 | // I am going to use a system where a sequence object has two methods: 2 | // 3 | // * next(), which returns a boolean indicating whether there are more 4 | // elements in the sequence, and moves it forward to the next 5 | // element when there are. 6 | // 7 | // * current(), which returns the current element, and should only be 8 | // called after next() has returned true at least once. 9 | 10 | function logFive(sequence) { 11 | for (var i = 0; i < 5; i++) { 12 | if (!sequence.next()) 13 | break; 14 | console.log(sequence.current()); 15 | } 16 | } 17 | 18 | function ArraySeq(array) { 19 | this.pos = -1; 20 | this.array = array; 21 | } 22 | ArraySeq.prototype.next = function() { 23 | if (this.pos >= this.array.length - 1) 24 | return false; 25 | this.pos++; 26 | return true; 27 | }; 28 | ArraySeq.prototype.current = function() { 29 | return this.array[this.pos]; 30 | }; 31 | 32 | function RangeSeq(from, to) { 33 | this.pos = from - 1; 34 | this.to = to; 35 | } 36 | RangeSeq.prototype.next = function() { 37 | if (this.pos >= this.to) 38 | return false; 39 | this.pos++; 40 | return true; 41 | }; 42 | RangeSeq.prototype.current = function() { 43 | return this.pos; 44 | }; 45 | 46 | logFive(new ArraySeq([1, 2])); 47 | // → 1 48 | // → 2 49 | logFive(new RangeSeq(100, 1000)); 50 | // → 100 51 | // → 101 52 | // → 102 53 | // → 103 54 | // → 104 55 | 56 | // This alternative approach represents the empty sequence as null, 57 | // and gives non-empty sequences two methods: 58 | // 59 | // * head() returns the element at the start of the sequence. 60 | // 61 | // * rest() returns the rest of the sequence, or null if there are no 62 | // elemements left. 63 | // 64 | // Because a JavaScript constructor can not return null, we add a make 65 | // function to constructors of this type of sequence, which constructs 66 | // a sequence, or returns null if the resulting sequence would be 67 | // empty. 68 | 69 | function logFive2(sequence) { 70 | for (var i = 0; i < 5 && sequence != null; i++) { 71 | console.log(sequence.head()); 72 | sequence = sequence.rest(); 73 | } 74 | } 75 | 76 | function ArraySeq2(array, offset) { 77 | this.array = array; 78 | this.offset = offset; 79 | } 80 | ArraySeq2.prototype.rest = function() { 81 | return ArraySeq2.make(this.array, this.offset + 1); 82 | }; 83 | ArraySeq2.prototype.head = function() { 84 | return this.array[this.offset]; 85 | }; 86 | ArraySeq2.make = function(array, offset) { 87 | if (offset == null) offset = 0; 88 | if (offset >= array.length) 89 | return null; 90 | else 91 | return new ArraySeq2(array, offset); 92 | }; 93 | 94 | function RangeSeq2(from, to) { 95 | this.from = from; 96 | this.to = to; 97 | } 98 | RangeSeq2.prototype.rest = function() { 99 | return RangeSeq2.make(this.from + 1, this.to); 100 | }; 101 | RangeSeq2.prototype.head = function() { 102 | return this.from; 103 | }; 104 | RangeSeq2.make = function(from, to) { 105 | if (from > to) 106 | return null; 107 | else 108 | return new RangeSeq2(from, to); 109 | }; 110 | 111 | logFive2(ArraySeq2.make([1, 2])); 112 | // → 1 113 | // → 2 114 | logFive2(RangeSeq2.make(100, 1000)); 115 | // → 100 116 | // → 101 117 | // → 102 118 | // → 103 119 | // → 104 120 | -------------------------------------------------------------------------------- /code/solutions/07_1_artificial_stupidity.js: -------------------------------------------------------------------------------- 1 | function SmartPlantEater() { 2 | this.energy = 30; 3 | this.direction = "e"; 4 | } 5 | SmartPlantEater.prototype.act = function(view) { 6 | var space = view.find(" "); 7 | if (this.energy > 90 && space) 8 | return {type: "reproduce", direction: space}; 9 | var plants = view.findAll("*"); 10 | if (plants.length > 1) 11 | return {type: "eat", direction: randomElement(plants)}; 12 | if (view.look(this.direction) != " " && space) 13 | this.direction = space; 14 | return {type: "move", direction: this.direction}; 15 | }; 16 | 17 | animateWorld(new LifelikeWorld( 18 | ["############################", 19 | "##### ######", 20 | "## *** **##", 21 | "# *##** ** O *##", 22 | "# *** O ##** *#", 23 | "# O ##*** #", 24 | "# ##** #", 25 | "# O #* #", 26 | "#* #** O #", 27 | "#*** ##** O **#", 28 | "##**** ###*** *###", 29 | "############################"], 30 | {"#": Wall, 31 | "O": SmartPlantEater, 32 | "*": Plant} 33 | )); 34 | -------------------------------------------------------------------------------- /code/solutions/07_2_predators.js: -------------------------------------------------------------------------------- 1 | function SmartPlantEater() { 2 | this.energy = 30; 3 | this.direction = "e"; 4 | } 5 | SmartPlantEater.prototype.act = function(view) { 6 | var space = view.find(" "); 7 | if (this.energy > 90 && space) 8 | return {type: "reproduce", direction: space}; 9 | var plants = view.findAll("*"); 10 | if (plants.length > 1) 11 | return {type: "eat", direction: randomElement(plants)}; 12 | if (view.look(this.direction) != " " && space) 13 | this.direction = space; 14 | return {type: "move", direction: this.direction}; 15 | }; 16 | 17 | function Tiger() { 18 | this.energy = 100; 19 | this.direction = "w"; 20 | // Used to track the amount of prey seen per turn in the last six turns 21 | this.preySeen = []; 22 | } 23 | Tiger.prototype.act = function(view) { 24 | // Average number of prey seen per turn 25 | var seenPerTurn = this.preySeen.reduce(function(a, b) { 26 | return a + b; 27 | }, 0) / this.preySeen.length; 28 | var prey = view.findAll("O"); 29 | this.preySeen.push(prey.length); 30 | // Drop the first element from the array when it is longer than 6 31 | if (this.preySeen.length > 6) 32 | this.preySeen.shift(); 33 | 34 | // Only eat if the predator saw more than ¼ prey animal per turn 35 | if (prey.length && seenPerTurn > 0.25) 36 | return {type: "eat", direction: randomElement(prey)}; 37 | 38 | var space = view.find(" "); 39 | if (this.energy > 400 && space) 40 | return {type: "reproduce", direction: space}; 41 | if (view.look(this.direction) != " " && space) 42 | this.direction = space; 43 | return {type: "move", direction: this.direction}; 44 | }; 45 | 46 | animateWorld(new LifelikeWorld( 47 | ["####################################################", 48 | "# #### **** ###", 49 | "# * @ ## ######## OO ##", 50 | "# * ## O O **** *#", 51 | "# ##* ########## *#", 52 | "# ##*** * **** **#", 53 | "#* ** # * *** ######### **#", 54 | "#* ** # * # * **#", 55 | "# ## # O # *** ######", 56 | "#* @ # # * O # #", 57 | "#* # ###### ** #", 58 | "### **** *** ** #", 59 | "# O @ O #", 60 | "# * ## ## ## ## ### * #", 61 | "# ** # * ##### O #", 62 | "## ** O O # # *** *** ### ** #", 63 | "### # ***** ****#", 64 | "####################################################"], 65 | {"#": Wall, 66 | "@": Tiger, 67 | "O": SmartPlantEater, // from previous exercise 68 | "*": Plant} 69 | )); 70 | -------------------------------------------------------------------------------- /code/solutions/08_1_retry.js: -------------------------------------------------------------------------------- 1 | function MultiplicatorUnitFailure() {} 2 | 3 | function primitiveMultiply(a, b) { 4 | if (Math.random() < 0.5) 5 | return a * b; 6 | else 7 | throw new MultiplicatorUnitFailure(); 8 | } 9 | 10 | function reliableMultiply(a, b) { 11 | for (;;) { 12 | try { 13 | return primitiveMultiply(a, b); 14 | } catch (e) { 15 | if (!(e instanceof MultiplicatorUnitFailure)) 16 | throw e; 17 | } 18 | } 19 | } 20 | 21 | console.log(reliableMultiply(8, 8)); 22 | // → 64 23 | 24 | -------------------------------------------------------------------------------- /code/solutions/08_2_the_locked_box.js: -------------------------------------------------------------------------------- 1 | function withBoxUnlocked(body) { 2 | var locked = box.locked; 3 | if (!locked) 4 | return body(); 5 | 6 | box.unlock(); 7 | try { 8 | return body(); 9 | } finally { 10 | box.lock(); 11 | } 12 | } 13 | 14 | withBoxUnlocked(function() { 15 | box.content.push("gold piece"); 16 | }); 17 | 18 | try { 19 | withBoxUnlocked(function() { 20 | throw new Error("Pirates on the horizon! Abort!"); 21 | }); 22 | } catch (e) { 23 | console.log("Error raised:", e); 24 | } 25 | 26 | console.log(box.locked); 27 | // → true 28 | -------------------------------------------------------------------------------- /code/solutions/09_1_regexp_golf.js: -------------------------------------------------------------------------------- 1 | // Fill in the regular expressions 2 | 3 | verify(/ca[rt]/, 4 | ["my car", "bad cats"], 5 | ["camper", "high art"]); 6 | 7 | verify(/pr?op/, 8 | ["pop culture", "mad props"], 9 | ["plop"]); 10 | 11 | verify(/ferr(et|y|ari)/, 12 | ["ferret", "ferry", "ferrari"], 13 | ["ferrum", "transfer A"]); 14 | 15 | verify(/ious\b/, 16 | ["how delicious", "spacious room"], 17 | ["ruinous", "consciousness"]); 18 | 19 | verify(/\s[.,:;]/, 20 | ["bad punctuation ."], 21 | ["escape the dot"]); 22 | 23 | verify(/\w{7,}/, 24 | ["hottentottententen"], 25 | ["no", "hotten totten tenten"]); 26 | 27 | verify(/\b[a-df-z]+\b/i, 28 | ["red platypus", "wobbling nest"], 29 | ["earth bed", "learning ape"]); 30 | 31 | 32 | function verify(regexp, yes, no) { 33 | // Ignore unfinished exercises 34 | if (regexp.source == "...") return; 35 | yes.forEach(function(s) { 36 | if (!regexp.test(s)) 37 | console.log("Failure to match '" + s + "'"); 38 | }); 39 | no.forEach(function(s) { 40 | if (regexp.test(s)) 41 | console.log("Unexpected match for '" + s + "'"); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /code/solutions/09_2_quoting_style.js: -------------------------------------------------------------------------------- 1 | var text = "'I'm the cook,' he said, 'it's my job.'"; 2 | 3 | console.log(text.replace(/(^|\W)'|'(\W|$)/g, '$1"$2')); 4 | // → "I'm the cook," he said, "it's my job." 5 | 6 | -------------------------------------------------------------------------------- /code/solutions/09_3_numbers_again.js: -------------------------------------------------------------------------------- 1 | // Fill in this regular expression. 2 | var number = /^(\+|-|)(\d+(\.\d*)?|\.\d+)([eE](\+|-|)\d+)?$/; 3 | 4 | // Tests: 5 | ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", 6 | "1e+12"].forEach(function(s) { 7 | if (!number.test(s)) 8 | console.log("Failed to match '" + s + "'"); 9 | }); 10 | ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", 11 | "."].forEach(function(s) { 12 | if (number.test(s)) 13 | console.log("Incorrectly accepted '" + s + "'"); 14 | }); 15 | -------------------------------------------------------------------------------- /code/solutions/10_1_month_names.js: -------------------------------------------------------------------------------- 1 | var month = function() { 2 | var names = ["January", "February", "March", "April", "May", 3 | "June", "July", "August", "September", "October", 4 | "November", "December"]; 5 | return { 6 | name: function(number) { return names[number]; }, 7 | number: function(name) { return names.indexOf(name); } 8 | }; 9 | }(); 10 | 11 | 12 | console.log(month.name(2)); 13 | // → March 14 | console.log(month.number("November")); 15 | // → 10 16 | -------------------------------------------------------------------------------- /code/solutions/11_1_arrays.js: -------------------------------------------------------------------------------- 1 | topEnv["array"] = function() { 2 | return Array.prototype.slice.call(arguments, 0); 3 | }; 4 | 5 | topEnv["length"] = function(array) { 6 | return array.length; 7 | }; 8 | 9 | topEnv["element"] = function(array, i) { 10 | return array[i]; 11 | }; 12 | 13 | run("do(define(sum, fun(array,", 14 | " do(define(i, 0),", 15 | " define(sum, 0),", 16 | " while(<(i, length(array)),", 17 | " do(define(sum, +(sum, element(array, i))),", 18 | " define(i, +(i, 1)))),", 19 | " sum))),", 20 | " print(sum(array(1, 2, 3))))"); 21 | // → 6 22 | -------------------------------------------------------------------------------- /code/solutions/11_3_comments.js: -------------------------------------------------------------------------------- 1 | function skipSpace(string) { 2 | var skippable = string.match(/^(\s|#.*)*/); 3 | return string.slice(skippable[0].length); 4 | } 5 | 6 | console.log(parse("# hello\nx")); 7 | // → {type: "word", name: "x"} 8 | 9 | console.log(parse("a # one\n # two\n()")); 10 | // → {type: "apply", 11 | // operator: {type: "word", name: "x"}, 12 | // args: []} 13 | -------------------------------------------------------------------------------- /code/solutions/11_4_fixing_scope.js: -------------------------------------------------------------------------------- 1 | specialForms["set"] = function(args, env) { 2 | if (args.length != 2 || args[0].type != "word") 3 | throw new SyntaxError("Bad use of set"); 4 | var varName = args[0].name; 5 | var value = evaluate(args[1], env); 6 | 7 | for (var scope = env; scope; scope = Object.getPrototypeOf(scope)) { 8 | if (Object.prototype.hasOwnProperty.call(scope, varName)) { 9 | scope[varName] = value; 10 | return value; 11 | } 12 | } 13 | throw new ReferenceError("Setting undefined variable " + varName); 14 | }; 15 | 16 | run("do(define(x, 4),", 17 | " define(setx, fun(val, set(x, val))),", 18 | " setx(50),", 19 | " print(x))"); 20 | // → 50 21 | run("set(quux, true)"); 22 | // → Some kind of ReferenceError 23 | -------------------------------------------------------------------------------- /code/solutions/13_1_build_a_table.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 44 | 45 | -------------------------------------------------------------------------------- /code/solutions/13_2_elements_by_tag_name.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

      Heading with a span element.

      4 |

      A paragraph with one, two 5 | spans.

      6 | 7 | 35 | -------------------------------------------------------------------------------- /code/solutions/13_3_the_cats_hat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /code/solutions/14_1_censored_keyboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 13 | -------------------------------------------------------------------------------- /code/solutions/14_2_mouse_trail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | 15 | 16 | 33 | 34 | -------------------------------------------------------------------------------- /code/solutions/14_3_tabs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
      4 |
      Tab one
      5 |
      Tab two
      6 |
      Tab three
      7 |
      8 | 44 | -------------------------------------------------------------------------------- /code/solutions/15_1_game_over.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 32 | 33 | -------------------------------------------------------------------------------- /code/solutions/15_2_pausing_the_game.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 89 | 90 | -------------------------------------------------------------------------------- /code/solutions/16_1_shapes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 67 | -------------------------------------------------------------------------------- /code/solutions/16_2_the_pie_chart.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 40 | -------------------------------------------------------------------------------- /code/solutions/16_3_a_bouncing_ball.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 38 | -------------------------------------------------------------------------------- /code/solutions/17_1_content_negotiation.js: -------------------------------------------------------------------------------- 1 | function requestAuthor(type) { 2 | var req = new XMLHttpRequest(); 3 | req.open("GET", "http://eloquentjavascript.net/author", false); 4 | req.setRequestHeader("accept", type); 5 | req.send(null); 6 | return req.responseText; 7 | } 8 | 9 | var types = ["text/plain", 10 | "text/html", 11 | "application/json", 12 | "application/rainbows+unicorns"]; 13 | 14 | types.forEach(function(type) { 15 | try { 16 | console.log(type + ":\n", requestAuthor(type), "\n"); 17 | } catch (e) { 18 | console.log("Raised error: " + e); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /code/solutions/17_2_waiting_for_multiple_promises.js: -------------------------------------------------------------------------------- 1 | function all(promises) { 2 | return new Promise(function(succeed, fail) { 3 | var results = [], pending = promises.length; 4 | promises.forEach(function(promise, i) { 5 | promise.then(function(result) { 6 | results[i] = result; 7 | pending -= 1; 8 | if (pending == 0) 9 | succeed(results); 10 | }, function(error) { 11 | fail(error); 12 | }); 13 | }); 14 | if (promises.length == 0) 15 | succeed(results); 16 | }); 17 | } 18 | 19 | // Test code. 20 | all([]).then(function(array) { 21 | console.log("This should be []:", array); 22 | }); 23 | function soon(val) { 24 | return new Promise(function(success) { 25 | setTimeout(function() { success(val); }, 26 | Math.random() * 500); 27 | }); 28 | } 29 | all([soon(1), soon(2), soon(3)]).then(function(array) { 30 | console.log("This should be [1, 2, 3]:", array); 31 | }); 32 | function fail() { 33 | return new Promise(function(success, fail) { 34 | fail(new Error("boom")); 35 | }); 36 | } 37 | all([soon(1), fail(), soon(3)]).then(function(array) { 38 | console.log("We should not get here"); 39 | }, function(error) { 40 | if (error.message != "boom") 41 | console.log("Unexpected failure:", error); 42 | }); 43 | -------------------------------------------------------------------------------- /code/solutions/18_1_a_javascript_workbench.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
      
       6 | 
       7 | 
      19 | 
      
      
      --------------------------------------------------------------------------------
      /code/solutions/18_2_autocompletion.html:
      --------------------------------------------------------------------------------
       1 | 
       2 | 
       3 | 
       4 | 
      5 | 6 | 32 | -------------------------------------------------------------------------------- /code/solutions/18_3_conways_game_of_life.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
      4 | 5 | 6 | 7 | 87 | -------------------------------------------------------------------------------- /code/solutions/19_1_rectangles.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /code/solutions/19_2_color_picker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /code/solutions/19_3_flood_fill.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /code/solutions/20_1_content_negotiation_again.js: -------------------------------------------------------------------------------- 1 | var http = require("http"); 2 | 3 | function readStreamAsString(stream, callback) { 4 | var content = ""; 5 | stream.on("data", function(chunk) { 6 | content += chunk; 7 | }); 8 | stream.on("end", function() { 9 | callback(null, content); 10 | }); 11 | stream.on("error", function(error) { 12 | callback(error); 13 | }); 14 | } 15 | 16 | ["text/plain", "text/html", "application/json"].forEach(function(type) { 17 | http.request({ 18 | hostname: "eloquentjavascript.net", 19 | path: "/author", 20 | headers: {Accept: type} 21 | }, function(response) { 22 | if (response.statusCode != 200) { 23 | console.error("Request for " + type + " failed: " + response.statusMessage); 24 | return; 25 | } 26 | readStreamAsString(response, function(error, content) { 27 | if (error) throw error; 28 | console.log("Type " + type + ": " + content); 29 | }); 30 | }).end(); 31 | }); 32 | -------------------------------------------------------------------------------- /code/solutions/20_2_fixing_a_leak.js: -------------------------------------------------------------------------------- 1 | // The file server examples in code/file_server.js and 2 | // code/file_server_promises.js already contain this fixed version. 3 | 4 | function urlToPath(url) { 5 | var path = require("url").parse(url).pathname; 6 | var decoded = decodeURIComponent(path); 7 | return "." + decoded.replace(/(\/|\\)\.\.(\/|\\|$)/g, "/"); 8 | } 9 | -------------------------------------------------------------------------------- /code/solutions/20_3_creating_directories.js: -------------------------------------------------------------------------------- 1 | // This code won't work on its own, but is also included in the 2 | // code/file_server.js file, which defines the whole system. 3 | 4 | methods.MKCOL = function(path, respond) { 5 | fs.stat(path, function(error, stats) { 6 | if (error && error.code == "ENOENT") 7 | fs.mkdir(path, respondErrorOrNothing(respond)); 8 | else if (error) 9 | respond(500, error.toString()); 10 | else if (stats.isDirectory()) 11 | respond(204); 12 | else 13 | respond(400, "File exists"); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /code/solutions/20_4_a_public_space_on_the_web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

      A Public Space on the Web

      4 | 5 |

      This is a self-editing website. Select a file, edit it, and save to 6 | update the website.

      7 | 8 |

      Files:

      9 | 10 |

      11 | 12 |
      13 | 14 |

      15 | 16 | 17 | -------------------------------------------------------------------------------- /code/solutions/20_4_a_public_space_on_the_web/other.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |

      This is another file

      4 | -------------------------------------------------------------------------------- /code/solutions/20_4_a_public_space_on_the_web/public_space.js: -------------------------------------------------------------------------------- 1 | // A somewhat node-ish HTTP request helper. 2 | function request(options, callback) { 3 | var req = new XMLHttpRequest(); 4 | req.open(options.method || "GET", options.pathname, true); 5 | req.addEventListener("load", function() { 6 | if (req.status < 400) 7 | callback(null, req.responseText); 8 | else 9 | callback(new Error("Request failed: " + req.statusText)); 10 | }); 11 | req.addEventListener("error", function() { 12 | callback(new Error("Network error")); 13 | }); 14 | req.send(options.body || null); 15 | } 16 | 17 | // Get a reference to the DOM nodes we need 18 | var filelist = document.querySelector("#filelist"); 19 | var textarea = document.querySelector("#file"); 20 | 21 | // This loads the initial file list from the server 22 | request({pathname: "/"}, function(error, files) { 23 | // We're not going to do error handling, but we want to at least 24 | // have errors show up in the console, since silently disappearing 25 | // errors make it very hard to debug a system. 26 | if (error) throw error; 27 | 28 | files.split("\n").forEach(function(file) { 29 | var option = document.createElement("option"); 30 | option.textContent = file; 31 | filelist.appendChild(option); 32 | }); 33 | // Now that we have a list of files, make sure the textarea contains 34 | // the currently selected one. 35 | loadCurrentFile(); 36 | }); 37 | 38 | // Fetch a file from the server and put it in the textarea. 39 | function loadCurrentFile() { 40 | request({pathname: filelist.value}, function(error, file) { 41 | if (error) throw error; 42 | textarea.value = file; 43 | }); 44 | } 45 | 46 | filelist.addEventListener("change", loadCurrentFile); 47 | 48 | // Called by the button on the page. Makes a request to save the 49 | // currently selected file. 50 | function saveFile() { 51 | request({pathname: filelist.value, 52 | method: "PUT", 53 | body: textarea.value}, function(error) { 54 | if (error) throw error; 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /code/solutions/21_1_disk_persistence.js: -------------------------------------------------------------------------------- 1 | // This isn't a stand-alone file, only a redefinition of a few 2 | // fragments from skillsharing/skillsharing_server.js 3 | 4 | var fs = require("fs"); 5 | 6 | var talks = loadTalks(); 7 | 8 | function loadTalks() { 9 | var result = Object.create(null), json; 10 | try { 11 | json = JSON.parse(fs.readFileSync("./talks.json", "utf8")); 12 | } catch (e) { 13 | json = {}; 14 | } 15 | for (var title in json) 16 | result[title] = json[title]; 17 | return result; 18 | } 19 | 20 | function registerChange(title) { 21 | changes.push({title: title, time: Date.now()}); 22 | waiting.forEach(function(waiter) { 23 | sendTalks(getChangedTalks(waiter.since), waiter.response); 24 | }); 25 | waiting = []; 26 | 27 | fs.writeFile("./talks.json", JSON.stringify(talks)); 28 | } 29 | -------------------------------------------------------------------------------- /code/solutions/21_2_comment_field_resets.js: -------------------------------------------------------------------------------- 1 | // This isn't a stand-alone file, only a redefinition of displayTalks 2 | // from skillsharing/public/skillsharing_client.js 3 | 4 | function displayTalks(talks) { 5 | talks.forEach(function(talk) { 6 | var shown = shownTalks[talk.title]; 7 | if (talk.deleted) { 8 | if (shown) { 9 | talkDiv.removeChild(shown); 10 | delete shownTalks[talk.title]; 11 | } 12 | } else { 13 | var node = drawTalk(talk); 14 | if (shown) { 15 | var textField = shown.querySelector("input"); 16 | var hasFocus = document.activeElement == textField; 17 | var value = textField.value; 18 | talkDiv.replaceChild(node, shown); 19 | var newTextField = node.querySelector("input"); 20 | newTextField.value = value; 21 | if (hasFocus) newTextField.focus(); 22 | } else { 23 | talkDiv.appendChild(node); 24 | } 25 | shownTalks[talk.title] = node; 26 | } 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /code/solutions/21_3_better_templates.js: -------------------------------------------------------------------------------- 1 | // Note that the first argument to instantiateTemplate was changed to 2 | // be the template itself, not its name, to make testing easier. 3 | 4 | function instantiateTemplate(template, values) { 5 | function instantiateText(text, values) { 6 | return text.replace(/\{\{(\w+)\}\}/g, function(_, name) { 7 | return values[name]; 8 | }); 9 | } 10 | function attr(node, attrName) { 11 | return node.nodeType == document.ELEMENT_NODE && 12 | node.getAttribute(attrName); 13 | } 14 | function instantiate(node, values) { 15 | if (node.nodeType == document.ELEMENT_NODE) { 16 | var copy = node.cloneNode(); 17 | for (var i = 0; i < node.childNodes.length; i++) { 18 | var child = node.childNodes[i]; 19 | 20 | var when = attr(child, "template-when"), unless = attr(child, "template-unless"); 21 | if (when && !values[when] || unless && values[unless]) 22 | continue; 23 | 24 | var repeat = attr(child, "template-repeat"); 25 | if (repeat) 26 | (values[repeat] || []).forEach(function(element) { 27 | copy.appendChild(instantiate(child, element)); 28 | }); 29 | else 30 | copy.appendChild(instantiate(child, values)); 31 | } 32 | return copy; 33 | } else if (node.nodeType == document.TEXT_NODE) { 34 | return document.createTextNode(instantiateText(node.nodeValue, values)); 35 | } 36 | } 37 | 38 | return instantiate(template, values); 39 | } 40 | 41 | // A simple test function to verify that the above actually works 42 | 43 | function test(template, values, expected) { 44 | var testTemplate = document.createElement("div"); 45 | testTemplate.innerHTML = template; 46 | var result = instantiateTemplate(testTemplate, values).innerHTML; 47 | if (result != expected) 48 | console.log("Unexpected instantiation. Expected\n " + expected + "\ngot\n " + result); 49 | } 50 | 51 | test('

      {{header}}

      ', 52 | {header: "One"}, 53 | '

      One

      '); 54 | 55 | test('

      {{header}}

      ', 56 | {header: false}, 57 | ''); 58 | 59 | test('

      Hello

      ', 60 | {noicon: true}, 61 | '

      Hello

      '); 62 | 63 | test('
      1. {{name}}: {{score}}
      ', 64 | {items: [{name: "Alice", score: "10"}, {name: "Bob", score: "7"}]}, 65 | '
      1. Alice: 10
      2. Bob: 7
      '); 66 | -------------------------------------------------------------------------------- /code/solutions/22_1_pathfinding.js: -------------------------------------------------------------------------------- 1 | function findPath(a, b) { 2 | var work = [[a]]; 3 | for (var i = 0; i < work.length; i++) { 4 | var cur = work[i], end = cur[cur.length - 1]; 5 | if (end == b) return cur; 6 | end.edges.forEach(function(next) { 7 | if (!work.some(function(work) { return work[work.length - 1] == next; })) 8 | work.push(cur.concat([next])); 9 | }); 10 | } 11 | } 12 | 13 | var graph = treeGraph(4, 4); 14 | var root = graph[0], leaf = graph[graph.length - 1]; 15 | console.log(findPath(root, leaf).length); 16 | // → 4 17 | 18 | leaf.connect(root); 19 | console.log(findPath(root, leaf).length); 20 | // → 2 21 | -------------------------------------------------------------------------------- /code/solutions/22_2_timing.js: -------------------------------------------------------------------------------- 1 | function findPath(a, b) { 2 | var work = [[a]]; 3 | for (var i = 0; i < work.length; i++) { 4 | var cur = work[i], end = cur[cur.length - 1]; 5 | if (end == b) return cur; 6 | end.edges.forEach(function(next) { 7 | if (!work.some(function(work) { return work[work.length - 1] == next; })) 8 | work.push(cur.concat([next])); 9 | }); 10 | } 11 | } 12 | 13 | var graph = treeGraph(6, 5); 14 | var startTime = Date.now(); 15 | console.log(findPath(graph[0], graph[graph.length - 1]).length); 16 | console.log("Time taken:", Date.now() - startTime); 17 | -------------------------------------------------------------------------------- /code/solutions/22_3_optimizing.js: -------------------------------------------------------------------------------- 1 | function withIDs(graph) { 2 | for (var i = 0; i < graph.length; i++) graph[i].id = i; 3 | return graph; 4 | } 5 | var graph = withIDs(treeGraph(8, 5)); 6 | 7 | function findPath_ids(a, b) { 8 | var work = [[a]]; 9 | var seen = Object.create(null); 10 | for (var i = 0; i < work.length; i++) { 11 | var cur = work[i], end = cur[cur.length - 1]; 12 | if (end == b) return cur; 13 | end.edges.forEach(function(next) { 14 | if (!seen[next.id]) { 15 | seen[next.id] = true; 16 | work.push(cur.concat([next])); 17 | } 18 | }); 19 | } 20 | } 21 | 22 | var startTime = Date.now(); 23 | console.log(findPath_ids(graph[0], graph[graph.length - 1]).length); 24 | console.log("Time taken with ids:", Date.now() - startTime); 25 | 26 | function listToArray(list) { 27 | var result = []; 28 | for (var cur = list; cur; cur = cur.via) 29 | result.unshift(cur.last); 30 | return result; 31 | } 32 | 33 | function findPath_list(a, b) { 34 | var work = [{last: a, via: null}]; 35 | var seen = Object.create(null); 36 | for (var i = 0; i < work.length; i++) { 37 | var cur = work[i]; 38 | if (cur.last == b) return listToArray(cur); 39 | cur.last.edges.forEach(function(next) { 40 | if (!seen[next.id]) { 41 | seen[next.id] = true; 42 | work.push({last: next, via: cur}); 43 | } 44 | }); 45 | } 46 | } 47 | 48 | var startTime = Date.now(); 49 | console.log(findPath_list(graph[0], graph[graph.length - 1]).length); 50 | console.log("Time taken with ids + lists:", Date.now() - startTime); 51 | -------------------------------------------------------------------------------- /code/squareworker.js: -------------------------------------------------------------------------------- 1 | addEventListener("message", function(event) { 2 | postMessage(event.data * event.data); 3 | }); 4 | -------------------------------------------------------------------------------- /epub/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/epub/.DS_Store -------------------------------------------------------------------------------- /epub/META-INF/container.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /epub/content.opf.src: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Eloquent JavaScript 5 | main 6 | Marijn Haverbeke 7 | HAVERBEKE, MARIJN 8 | aut 9 | net.eloquentjavascript 10 | en-US 11 | This work is shared with the public using the Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0) license. 12 | 13 | http://eloquentjavascript.net/ 14 | 2014-09-04T10:47:00Z 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {{images}} 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /epub/font/cinzel_bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/epub/font/cinzel_bold.otf -------------------------------------------------------------------------------- /epub/font/pt_mono.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/epub/font/pt_mono.otf -------------------------------------------------------------------------------- /epub/frontmatter.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Eloquent JavaScript 6 | 7 | 8 |

      Eloquent JavaScript

      9 |

      Written by Marijn Haverbeke.

      10 | 11 |

      Licensed under 12 | a Creative 13 | Commons attribution-noncommercial license. All code in this book 14 | may also be considered licensed under 15 | an MIT license.

      16 | 17 |

      Illustrations by various artists: Cover 18 | by Wasif Hyder. Computer (introduction) and unicycle people 19 | (Chapter 21) by Max Xiantu. Sea of bits (Chapter 1) and 20 | weresquirrel (Chapter 4) by Margarita Martínez and José Menor. 21 | Octopuses (Chapter 2 and 4) by Jim Tierney. Object with on/off 22 | switch (Chapter 6) by Dyle MacGregor. Regular expression diagrams 23 | in Chapter 9 generated 24 | with regexper.com by Jeff 25 | Avallone. Game concept for Chapter 15 26 | by Thomas Palef. Pixel art in 27 | Chapter 16 by Antonio Perdomo Pastor.

      28 | 29 |

      The second edition of Eloquent JavaScript was made possible 30 | by 454 financial backers.

      31 | 32 |

      A paper version of Eloquent JavaScript, including a bonus 33 | chapter, is being brought out 34 | by No Starch Press. 35 | They also sell a more polished EPUB version that includes the bonus chapter.

      36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /epub/mimetype: -------------------------------------------------------------------------------- 1 | application/epub+zip -------------------------------------------------------------------------------- /epub/style.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Cinzel'; 3 | font-style: normal; 4 | font-weight: 700; 5 | src: url(font/cinzel_bold.otf); 6 | } 7 | 8 | @font-face { 9 | font-family: 'PT Mono'; 10 | font-style: normal; 11 | font-weight: 400; 12 | src: url(font/pt_mono.otf); 13 | } 14 | 15 | body { 16 | font-family: Georgia, 'Nimbus Roman No9 L', 'Century Schoolbook L', serif; 17 | font-size: 20px; 18 | line-height: 1.45; 19 | color: black; 20 | background: white; 21 | text-align: left; 22 | } 23 | 24 | article { 25 | padding: 2em 0 5em 0; 26 | } 27 | 28 | pre { 29 | padding: 5px 0 5px 15px; 30 | line-height: 1.35; 31 | margin: 1rem 0; 32 | position: relative; 33 | font-size: 16px; 34 | } 35 | 36 | code, pre { 37 | font-family: 'PT Mono', monospace; 38 | } 39 | 40 | code { 41 | font-size: 18px; 42 | padding: 0 2px; 43 | } 44 | 45 | h1, h2, h3 { 46 | font-family: 'Cinzel', Georgia, serif; 47 | font-weight: 700; 48 | margin: 1rem 0; 49 | letter-spacing: 2px; 50 | } 51 | 52 | h1 { 53 | font-size: 130%; 54 | } 55 | h2 { 56 | font-size: 115%; 57 | } 58 | h3 { 59 | font-size: 100%; 60 | } 61 | 62 | span.chap_num { 63 | display: block; 64 | font-size: 60%; 65 | color: #aaa; 66 | margin-top: -.7em; 67 | } 68 | 69 | blockquote { 70 | margin: 0 0 0 3em; 71 | padding: 0; 72 | position: relative; 73 | font-size: 85%; 74 | } 75 | 76 | blockquote p { 77 | color: #333; 78 | } 79 | 80 | blockquote:before { 81 | content: '“'; 82 | position: absolute; 83 | left: -.5em; 84 | } 85 | 86 | blockquote footer { 87 | position: relative; 88 | margin-left: 1em; 89 | } 90 | 91 | p + footer { 92 | margin-top: -.5em; 93 | } 94 | 95 | blockquote footer cite { 96 | font-style: italic; 97 | } 98 | 99 | blockquote footer:before { 100 | content: '—'; 101 | position: absolute; 102 | left: -1em; 103 | } 104 | 105 | div.image img { 106 | max-width: 80%; 107 | margin-left: 30px; 108 | } 109 | 110 | td { 111 | vertical-align: top; 112 | } 113 | 114 | td + td { 115 | padding-left: 1em; 116 | } 117 | 118 | table { 119 | margin-left: 15px; 120 | } 121 | 122 | /* Syntax highlighting */ 123 | .cm-keyword {color: #506;} 124 | .cm-atom {color: #106;} 125 | .cm-number {color: #042;} 126 | .cm-def {color: #009;} 127 | .cm-variable-2, .cm-attribute {color: #027;} 128 | .cm-variable-3 {color: #072;} 129 | .cm-comment {color: #740;} 130 | .cm-string {color: #700;} 131 | .cm-string-2 {color: #740;} 132 | .cm-tag {color: #170;} 133 | .cm-keyword {color: #708;} 134 | .cm-atom {color: #219;} 135 | .cm-number {color: #164;} 136 | .cm-def {color: #00f;} 137 | -------------------------------------------------------------------------------- /epub/titlepage.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Cover 5 | 6 | 10 | 11 | 12 |
      13 | cover 14 |
      15 | 16 | 17 | -------------------------------------------------------------------------------- /epub/toc.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Table of Contents 5 | 6 | 39 | 40 | 41 | 42 |

      Table of Contents

      43 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /html/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/.DS_Store -------------------------------------------------------------------------------- /html/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/.keep -------------------------------------------------------------------------------- /html/author.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
      4 |

      Marijn Haverbeke, Programmer

      6 | 7 |

      You can reach me at marijn@haverbeke.nl, or visit my web page, marijnhaverbeke.nl.

      10 |
      11 | -------------------------------------------------------------------------------- /html/author.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Marijn Haverbeke", 3 | "email": "marijn@haverbeke.nl", 4 | "website": "http://marijnhaverbeke.nl" 5 | } 6 | -------------------------------------------------------------------------------- /html/author.txt: -------------------------------------------------------------------------------- 1 | My name is Marijn Haverbeke. You can email me at marijn@haverbeke.nl, or visit my website, http://marijnhaverbeke.nl . 2 | -------------------------------------------------------------------------------- /html/code: -------------------------------------------------------------------------------- 1 | ../code -------------------------------------------------------------------------------- /html/css/game.css: -------------------------------------------------------------------------------- 1 | .background { background: rgb(52, 166, 251); 2 | table-layout: fixed; 3 | border-spacing: 0; } 4 | .background td { padding: 0; } 5 | .lava { background: rgb(255, 100, 100); } 6 | .wall { background: white; } 7 | 8 | .actor { position: absolute; } 9 | .coin { background: rgb(241, 229, 89); } 10 | .player { background: rgb(64, 64, 64); } 11 | 12 | .game { 13 | overflow: hidden; 14 | max-width: 600px; 15 | max-height: 450px; 16 | position: relative; 17 | } 18 | 19 | .lost .player { 20 | background: rgb(160, 64, 64); 21 | } 22 | .won .player { 23 | box-shadow: -4px -7px 8px white, 4px -7px 8px white; 24 | } 25 | -------------------------------------------------------------------------------- /html/css/paint.css: -------------------------------------------------------------------------------- 1 | .picturepanel { 2 | width: -webkit-fit-content; 3 | width: -moz-fit-content; 4 | width: -ms-fit-content; 5 | width: fit-content; 6 | max-width: 500px; 7 | max-height: 300px; 8 | border: 2px solid silver; 9 | overflow: auto; 10 | position: relative; 11 | } 12 | .picturepanel canvas { display: block; } 13 | .toolbar > * { margin-right: 5px; } 14 | -------------------------------------------------------------------------------- /html/empty.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /html/errata.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Eloquent JavaScript :: Errata 5 | 6 | 7 | 9 | 10 | 11 |
      12 | 13 |

      Errata

      14 | 15 |

      These are the known mistakes in the second edition 16 | of the book. For errata in the first edition, 17 | see this 18 | page. To report a problem that is not listed 19 | here, send me an email.

      20 | 21 |

      Issues whose section title is followed by a superscript ¹ were 22 | fixed after the first print of the book, and will not be present in 23 | later prints. Those followed by ² were fixed in the third print.

      24 | 25 |

      Introduction

      26 | 27 |

      Page 4 (Why Language Matters¹): The sentence “You 28 | can probably imagine how how tedious…” duplicates the 29 | word how.

      30 | 31 |

      Chapter 2

      32 | 33 |

      Page 32 (while and do Loops²): The var 34 | name example unintentionally exits the loop when the dialog is 35 | canceled, because browsers have a built-in global 36 | variable name whose values are implicitly converted to 37 | strings.

      38 | 39 |

      Chapter 9

      40 | 41 |

      Page 157 (Repeating Parts of a Pattern): The 42 | paragraph at the top of the page claims that {,5} is equivalent to 43 | {0,5} in a regular expression. This is not the case (you 44 | have to include the zero).

      45 | 46 |

      Page 159 (The Date Type¹): 47 | The findDate function produces the wrong months. The 48 | second argument given to new Date should 49 | be Number(match[2]) - 1, subtracting one to compensate 50 | for the fact that months start at zero in this interface.

      51 | 52 |

      Page 162 (Backtracking²): The regular expression 53 | should have another + sign after the [\da-f] 54 | group to match what it is described as matching. The diagram for the 55 | expression is similarly missing an arrow looping back around the 56 | corresponding box.

      57 | 58 |

      Chapter 10

      59 | 60 |

      Page 185 (Slow-Loading Modules¹): In the code 61 | snippet for define, the second use of 62 | the every method is incorrect. The call should be 63 | to myMod.onLoad.forEach instead.

      64 | 65 |

      Chapter 11

      66 | 67 |

      Page 192 (Parsing¹): The example Egg program has 68 | its parentheses distributed incorrectly. One closing parentheses from 69 | the second line should be moved to the end of the last line.

      70 | 71 |

      Chapter 12

      72 | 73 |

      Page 209 (The Web²): In the third paragraph I claim 74 | that the U in “URL” stands for Universal, whereas it actually 75 | stands for Uniform.

      76 | 77 |

      Chapter 14

      78 | 79 |

      Page 242 (Mouse Motion¹): The example 80 | uses event.which to detect mouse button release. This 81 | only works in Chrome and Safari. See 82 | the updated code 83 | and comment for a better 84 | way.

      85 | 86 |

      Chapter 16

      87 | 88 |

      Page 281 (Curves¹): The text refers to 89 | the bezierCurve method. This method is actually 90 | called bezierCurveTo (as in the example code).

      91 | 92 |

      Page 282 (Curves¹): Where it says the picture shows 93 | a line from the left of the circle to the left of the quarter circle, 94 | it should definitely say right in both instances.

      95 | 96 |

      Chapter 17

      97 | 98 |

      Page 312 (Summary¹): The code example uses 99 | the statusCode property. This does not exist, since the 100 | property is actually called status.

      101 | 102 |

      Chapter 18

      103 | 104 |

      Page 316 (Fields²): The checked 105 | attribute in the file field at the bottom of the example 106 | doesn't mean anything, and should not be there.

      107 | 108 |

      Page 327 (Summary¹): “When the user has selected 109 | a field” should be “When the user has selected 110 | a file”. 111 | 112 |

      113 | -------------------------------------------------------------------------------- /html/example/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/example/.DS_Store -------------------------------------------------------------------------------- /html/example/bert.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Bert", 3 | "spouse": "example/suzie.json" 4 | } 5 | -------------------------------------------------------------------------------- /html/example/data.txt: -------------------------------------------------------------------------------- 1 | This is the content of data.txt 2 | -------------------------------------------------------------------------------- /html/example/fruit.json: -------------------------------------------------------------------------------- 1 | {"banana": "yellow", 2 | "lemon": "yellow", 3 | "cherry": "red"} 4 | -------------------------------------------------------------------------------- /html/example/fruit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /html/example/message.html: -------------------------------------------------------------------------------- 1 | 2 | Example form target 3 | 4 | 5 | 6 | 10 | 13 | 14 |

      (Note that this page is just an illustration. No actual message was delivered anywhere.)

      15 | 16 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /html/example/muriel.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Muriel" 3 | } 4 | -------------------------------------------------------------------------------- /html/example/submit.html: -------------------------------------------------------------------------------- 1 | 2 | Example form target 3 | 4 | 5 | 6 |

      You submitted:

      7 | 8 |
      9 | 10 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /html/example/suzie.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Suzie", 3 | "spouse": "example/bert.json", 4 | "mother": "example/muriel.json" 5 | } 6 | -------------------------------------------------------------------------------- /html/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/favicon.ico -------------------------------------------------------------------------------- /html/font/cinzel_bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/font/cinzel_bold.woff -------------------------------------------------------------------------------- /html/font/pt_mono.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/html/font/pt_mono.woff -------------------------------------------------------------------------------- /html/img: -------------------------------------------------------------------------------- 1 | ../img/ -------------------------------------------------------------------------------- /html/js/.tern-project: -------------------------------------------------------------------------------- 1 | { 2 | "libs": ["browser"] 3 | } -------------------------------------------------------------------------------- /html/js/node_modules: -------------------------------------------------------------------------------- 1 | ../../node_modules/ -------------------------------------------------------------------------------- /img/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/.DS_Store -------------------------------------------------------------------------------- /img/alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/alert.png -------------------------------------------------------------------------------- /img/bit-sea.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/bit-sea.png -------------------------------------------------------------------------------- /img/blockquote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/blockquote.png -------------------------------------------------------------------------------- /img/boxed-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/boxed-in.png -------------------------------------------------------------------------------- /img/button_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/button_disabled.png -------------------------------------------------------------------------------- /img/canvas_arc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_arc.png -------------------------------------------------------------------------------- /img/canvas_beziercurve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_beziercurve.png -------------------------------------------------------------------------------- /img/canvas_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_circle.png -------------------------------------------------------------------------------- /img/canvas_fill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_fill.png -------------------------------------------------------------------------------- /img/canvas_game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_game.png -------------------------------------------------------------------------------- /img/canvas_path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_path.png -------------------------------------------------------------------------------- /img/canvas_pie_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_pie_chart.png -------------------------------------------------------------------------------- /img/canvas_quadraticcurve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_quadraticcurve.png -------------------------------------------------------------------------------- /img/canvas_scale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_scale.png -------------------------------------------------------------------------------- /img/canvas_stroke.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_stroke.png -------------------------------------------------------------------------------- /img/canvas_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_tree.png -------------------------------------------------------------------------------- /img/canvas_triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/canvas_triangle.png -------------------------------------------------------------------------------- /img/cat-animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/cat-animation.png -------------------------------------------------------------------------------- /img/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/cat.png -------------------------------------------------------------------------------- /img/color-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/color-field.png -------------------------------------------------------------------------------- /img/colored-links.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/colored-links.png -------------------------------------------------------------------------------- /img/confirm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/confirm.png -------------------------------------------------------------------------------- /img/control-io.svg: -------------------------------------------------------------------------------- 1 | 2 | synchronous, single thread of controlsynchronous, two threads of controlasynchronous -------------------------------------------------------------------------------- /img/controlflow-straight.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 20 | 27 | 33 | 34 | 35 | 57 | 59 | 60 | 62 | image/svg+xml 63 | 65 | 66 | 67 | 68 | 69 | 74 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /img/cos_sin.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | cos(¼π)sin(¼π)cos(-⅔π)sin(-⅔π) -------------------------------------------------------------------------------- /img/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/cover.jpg -------------------------------------------------------------------------------- /img/cover_.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/cover_.png -------------------------------------------------------------------------------- /img/darkblue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/darkblue.png -------------------------------------------------------------------------------- /img/display.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/display.png -------------------------------------------------------------------------------- /img/drag-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/drag-bar.png -------------------------------------------------------------------------------- /img/exercise_shapes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/exercise_shapes.png -------------------------------------------------------------------------------- /img/flood-grid.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/form_fields.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/form_fields.png -------------------------------------------------------------------------------- /img/form_multi_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/form_multi_select.png -------------------------------------------------------------------------------- /img/form_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/form_select.png -------------------------------------------------------------------------------- /img/game-grid.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/game_simpleLevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/game_simpleLevel.png -------------------------------------------------------------------------------- /img/generated/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/generated/.keep -------------------------------------------------------------------------------- /img/ghostery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/ghostery.png -------------------------------------------------------------------------------- /img/ghostery_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/ghostery_mini.png -------------------------------------------------------------------------------- /img/hack_reactor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/hack_reactor.png -------------------------------------------------------------------------------- /img/hack_reactor_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/hack_reactor_mini.png -------------------------------------------------------------------------------- /img/hat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/hat.png -------------------------------------------------------------------------------- /img/help-field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/help-field.png -------------------------------------------------------------------------------- /img/highlighted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/highlighted.png -------------------------------------------------------------------------------- /img/home-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/home-page.png -------------------------------------------------------------------------------- /img/html-boxes.svg: -------------------------------------------------------------------------------- 1 | 2 | 23 | herea.I also wrote a book! Read itpHello, I am Marijn and this is...pMy home pageh1bodyMy home pagetitleheadhtml -------------------------------------------------------------------------------- /img/html-links.svg: -------------------------------------------------------------------------------- 1 | 2 | 25 | I also wrote a book! ...pHello, I am Marijn...pMy home pageh1body012childNodesfirstChildlastChildpreviousSiblingnextSiblingparentNode -------------------------------------------------------------------------------- /img/linked-list.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | value: 1rest:value: 2rest:value: 3rest: null -------------------------------------------------------------------------------- /img/mirror.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | mirror1234 -------------------------------------------------------------------------------- /img/mozilla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/mozilla.png -------------------------------------------------------------------------------- /img/mozilla_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/mozilla_mini.png -------------------------------------------------------------------------------- /img/object.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/object.jpg -------------------------------------------------------------------------------- /img/object_full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/object_full.jpg -------------------------------------------------------------------------------- /img/octopus-array-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus-array-big.jpg -------------------------------------------------------------------------------- /img/octopus-array.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus-array.jpg -------------------------------------------------------------------------------- /img/octopus-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus-big.jpg -------------------------------------------------------------------------------- /img/octopus-object-big.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus-object-big.jpg -------------------------------------------------------------------------------- /img/octopus-object.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus-object.jpg -------------------------------------------------------------------------------- /img/octopus.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/octopus.jpg -------------------------------------------------------------------------------- /img/ostrich.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/ostrich.png -------------------------------------------------------------------------------- /img/paint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/paint.png -------------------------------------------------------------------------------- /img/pizza-squirrel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | No squirrel, no pizza76Squirrel, no pizza4No squirrel, pizza9Squirrel, pizza1 -------------------------------------------------------------------------------- /img/player.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/player.png -------------------------------------------------------------------------------- /img/player_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/player_big.png -------------------------------------------------------------------------------- /img/prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/prompt.png -------------------------------------------------------------------------------- /img/rabbits.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | toString: <function>...teeth: "small"speak: <function>killerRabbitteeth: "long, sharp, ..."adjective: "killer"RabbitprototypeObjectcreate: <function>prototype... -------------------------------------------------------------------------------- /img/skillsharing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/skillsharing.png -------------------------------------------------------------------------------- /img/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/sprites.png -------------------------------------------------------------------------------- /img/sprites_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/sprites_big.png -------------------------------------------------------------------------------- /img/svg-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/svg-demo.png -------------------------------------------------------------------------------- /img/syntax_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | dodefinex10if>x5print"large"print"small" -------------------------------------------------------------------------------- /img/transform.svg: -------------------------------------------------------------------------------- 1 | 2 | 11 | translate(50, 50)rotate(0.1*Math.PI)rotate(0.1*Math.PI)translate(50, 50) -------------------------------------------------------------------------------- /img/weresquirrel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/img/weresquirrel.png -------------------------------------------------------------------------------- /mkcopy: -------------------------------------------------------------------------------- 1 | make html 2 | yes | cp -rf html/* ../Eloquent-Javscritp-es-online/chapters -------------------------------------------------------------------------------- /nostarch/book.tex: -------------------------------------------------------------------------------- 1 | \documentclass[cfonts]{nostarch} 2 | \usepackage{natbib} 3 | \usepackage{nshyper} 4 | 5 | % There has to be a better way do to this, but after wasting a full 6 | % day, I wasn't able to find it (using the fontspec package kills the 7 | % nostarch style) 8 | \usepackage{newunicodechar} 9 | \newunicodechar{→}{$\triangleright$} 10 | \newunicodechar{←}{$\leftarrow$} 11 | \newunicodechar{“}{``} 12 | \newunicodechar{”}{''} 13 | \newunicodechar{’}{'} 14 | \newunicodechar{π}{$\pi$} 15 | \newunicodechar{½}{$\frac{1}{2}$} 16 | \newunicodechar{⅓}{$\frac{1}{3}$} 17 | \newunicodechar{¼}{$\frac{1}{4}$} 18 | \newunicodechar{…}{...} 19 | \newunicodechar{×}{$\times$} 20 | \newunicodechar{β}{\ss} 21 | \newunicodechar{ϕ}{$\varphi$} 22 | \newunicodechar{≈}{$\approx$} 23 | \newunicodechar{—}{---} 24 | 25 | % epigraph is used to style chapter quotes 26 | \usepackage{epigraph} 27 | \setlength{\epigraphwidth}{.8\textwidth} 28 | \setlength{\epigraphrule}{0pt} 29 | 30 | \graphicspath{ {../} } 31 | \makeindex 32 | 33 | \begin{document} 34 | 35 | \frontmatter 36 | 37 | \author{Marijn Haverbeke} 38 | 39 | \title{Eloquent JavaScript} 40 | 41 | \subtitle{A Modern Introduction to Programming} 42 | 43 | \makehalftitle 44 | 45 | \maketitle 46 | 47 | \begin{copyrightpage} 48 | \textbf{\sffamily\MakeUppercase{Pellentesque habitant morbi.}} 49 | Copyright \textcopyright{} 2014 by Marijn Haverbeke 50 | 51 | This work is licensed under a Creative Commons 52 | attribution-noncommercial license 53 | (\url{http://creativecommons.org/licenses/by-nc/3.0/}). 54 | 55 | For information on book distributors or translations, please contact 56 | No Starch Press, Inc. directly: \\ 57 | No Starch Press, Inc.\\ 58 | 555 De Haro Street, Suite 250, San Francisco, CA 94107\\ 59 | phone: 415.863.9900; fax: 415.863.9950; info@nostarch.com; www.nostarch.com\\ 60 | 61 | No Starch Press and the No Starch Press logo are registered trademarks 62 | of No Starch Press, Inc. Other product and company names mentioned 63 | herein may be the trademarks of their respective owners. Rather than 64 | use a trademark symbol with every occurrence of a trademarked name, we 65 | are using the names only in an editorial fashion and to the benefit of 66 | the trademark owner, with no intention of infringement of the 67 | trademark. 68 | 69 | The information in this book is distributed on an ``As Is'' basis, 70 | without warranty. While every precaution has been taken in the 71 | preparation of this work, neither the author nor No Starch Press, Inc. 72 | shall have any liability to any person or entity with respect to any 73 | loss or damage caused or alleged to be caused directly or indirectly 74 | by the information contained in it. 75 | 76 | \end{copyrightpage} 77 | 78 | \begin{dedicationpage} 79 | For Lotte and Jan 80 | \end{dedicationpage} 81 | 82 | \brieftableofcontents 83 | 84 | \tableofcontents 85 | 86 | \mainmatter 87 | 88 | \input{00_intro.tex} 89 | 90 | \input{01_values.tex} 91 | 92 | \input{02_program_structure.tex} 93 | 94 | \input{03_functions.tex} 95 | 96 | \input{04_data.tex} 97 | 98 | \input{05_higher_order.tex} 99 | 100 | \input{06_object.tex} 101 | 102 | \input{07_elife.tex} 103 | 104 | \input{08_error.tex} 105 | 106 | \input{09_regexp.tex} 107 | 108 | \input{10_modules.tex} 109 | 110 | \input{11_language.tex} 111 | 112 | \input{12_browser.tex} 113 | 114 | \input{13_dom.tex} 115 | 116 | \input{14_event.tex} 117 | 118 | \input{15_game.tex} 119 | 120 | \input{16_canvas.tex} 121 | 122 | \input{17_http.tex} 123 | 124 | \input{18_forms.tex} 125 | 126 | \input{19_paint.tex} 127 | 128 | \input{20_node.tex} 129 | 130 | \input{21_skillsharing.tex} 131 | 132 | \input{hints.tex} 133 | 134 | \backmatter 135 | 136 | \printindex 137 | 138 | \updatespage 139 | 140 | Visit \url{http://eloquentjavascript.net} for solutions to the 141 | exercises, updates, errata, and other information. 142 | 143 | \end{document} 144 | -------------------------------------------------------------------------------- /nostarch/build.sh: -------------------------------------------------------------------------------- 1 | xelatex book.tex 2 | xelatex book.tex 3 | makeindex -s nostarch.ist -o book.ind book.idx 4 | makeindex -s nostarch.ist -o book.ind book.idx 5 | xelatex book.tex 6 | while ( grep -q '^LaTeX Warning: Label(s) may have changed' book.log) \ 7 | do xelatex book.tex; done 8 | -------------------------------------------------------------------------------- /nostarch/nostarch.ins: -------------------------------------------------------------------------------- 1 | % 2 | % Doctrip file for nostarch 3 | % This file is in public domain 4 | % $Id: nostarch.ins,v 1.3 2008-03-23 21:48:10 boris Exp $ 5 | % 6 | \def\batchfile{nostarch.ins} 7 | \input docstrip 8 | \keepsilent 9 | \showprogress 10 | 11 | 12 | \askforoverwritefalse 13 | 14 | \generate{% 15 | \file{nostarch.cls}{\from{nostarch.dtx}{class}} 16 | \file{nshyper.sty}{\from{nostarch.dtx}{nshyper}} 17 | \file{nostarch.ist}{\from{nostarch.dtx}{ist}}} 18 | 19 | \obeyspaces 20 | \Msg{*****************************************************}% 21 | \Msg{* Congratulations! You successfully generated the *}% 22 | \Msg{* nostarch package. *}% 23 | \Msg{* *}% 24 | \Msg{* Please move the file nostarch.cls and nshyper.sty *}% 25 | \Msg{* to the place where the LaTeX files are stored on *}% 26 | \Msg{* your system. Move the file nostarch.ist to the *}% 27 | \Msg{* place where MakeIndex styles are storen on your *}% 28 | \Msg{* system. The manual is nostarch.pdf . *}% 29 | \Msg{* *}% 30 | \Msg{* The package is released under LPPL *}% 31 | \Msg{* *}% 32 | \Msg{* Happy TeXing! *}% 33 | \Msg{*****************************************************}% -------------------------------------------------------------------------------- /nostarch/nostarch.ist: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `nostarch.ist', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% nostarch.dtx (with options: `ist') 8 | %% 9 | %% IMPORTANT NOTICE: 10 | %% 11 | %% For the copyright see the source file. 12 | %% 13 | %% Any modified versions of this file must be renamed 14 | %% with new filenames distinct from nostarch.ist. 15 | %% 16 | %% For distribution of the original source see the terms 17 | %% for copying and modification in the file nostarch.dtx. 18 | %% 19 | %% This generated file may be distributed as long as the 20 | %% original source files, as listed above, are part of the 21 | %% same distribution. (The sources need not necessarily be 22 | %% in the same archive or directory.) 23 | %% \CharacterTable 24 | %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z 25 | %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z 26 | %% Digits \0\1\2\3\4\5\6\7\8\9 27 | %% Exclamation \! Double quote \" Hash (number) \# 28 | %% Dollar \$ Percent \% Ampersand \& 29 | %% Acute accent \' Left paren \( Right paren \) 30 | %% Asterisk \* Plus \+ Comma \, 31 | %% Minus \- Point \. Solidus \/ 32 | %% Colon \: Semicolon \; Less than \< 33 | %% Equals \= Greater than \> Question mark \? 34 | %% Commercial at \@ Left bracket \[ Backslash \\ 35 | %% Right bracket \] Circumflex \^ Underscore \_ 36 | %% Grave accent \` Left brace \{ Vertical bar \| 37 | %% Right brace \} Tilde \~} 38 | lethead_prefix "\\indexgroup{" 39 | lethead_suffix "}\\nopagebreak\n" 40 | lethead_flag 1 41 | heading_prefix "\\indexgroup{" 42 | heading_suffix "}\\nopagebreak\n" 43 | headings_flag 1 44 | \endinput 45 | %% 46 | %% End of file `nostarch.ist'. 47 | -------------------------------------------------------------------------------- /nostarch/nshyper.sty: -------------------------------------------------------------------------------- 1 | %% 2 | %% This is file `nshyper.sty', 3 | %% generated with the docstrip utility. 4 | %% 5 | %% The original source files were: 6 | %% 7 | %% nostarch.dtx (with options: `nshyper') 8 | %% 9 | %% IMPORTANT NOTICE: 10 | %% 11 | %% For the copyright see the source file. 12 | %% 13 | %% Any modified versions of this file must be renamed 14 | %% with new filenames distinct from nshyper.sty. 15 | %% 16 | %% For distribution of the original source see the terms 17 | %% for copying and modification in the file nostarch.dtx. 18 | %% 19 | %% This generated file may be distributed as long as the 20 | %% original source files, as listed above, are part of the 21 | %% same distribution. (The sources need not necessarily be 22 | %% in the same archive or directory.) 23 | %% \CharacterTable 24 | %% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z 25 | %% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z 26 | %% Digits \0\1\2\3\4\5\6\7\8\9 27 | %% Exclamation \! Double quote \" Hash (number) \# 28 | %% Dollar \$ Percent \% Ampersand \& 29 | %% Acute accent \' Left paren \( Right paren \) 30 | %% Asterisk \* Plus \+ Comma \, 31 | %% Minus \- Point \. Solidus \/ 32 | %% Colon \: Semicolon \; Less than \< 33 | %% Equals \= Greater than \> Question mark \? 34 | %% Commercial at \@ Left bracket \[ Backslash \\ 35 | %% Right bracket \] Circumflex \^ Underscore \_ 36 | %% Grave accent \` Left brace \{ Vertical bar \| 37 | %% Right brace \} Tilde \~} 38 | \ProvidesPackage{nshyper} 39 | [2008/06/06 v1.3 Typesetting books for No Starch Press] 40 | \RequirePackage[breaklinks,colorlinks,linkcolor=black, 41 | citecolor=black,pagecolor=black,urlcolor=black,hyperindex, 42 | bookmarks=false]{hyperref} 43 | \AtBeginDocument{% 44 | \def\@schapter[#1]#2{% 45 | \H@old@schapter[#1]{#2}% 46 | \@nschapterpreamble}% 47 | \def\@spart[#1]#2{% 48 | \H@old@spart[#1]{#2}% 49 | \Hy@GlobalStepCount\Hy@linkcounter 50 | \xdef\@currentHref{part*.\the\Hy@linkcounter}% 51 | \Hy@raisedlink{\hyper@anchorstart{\@currentHref}\hyper@anchorend}}}% 52 | \def\@nschapterpreamble{% 53 | \begingroup 54 | \let\@mkboth\@gobbletwo 55 | \Hy@GlobalStepCount\Hy@linkcounter 56 | \xdef\@currentHref{\Hy@chapapp*.\the\Hy@linkcounter}% 57 | \Hy@raisedlink{\hyper@anchorstart{\@currentHref}\hyper@anchorend}% 58 | \endgroup} 59 | \let\ns@old@maketitle\maketitle 60 | \def\maketitle{% 61 | \hypersetup{pdfauthor=\@author, pdftitle=\@title}% 62 | \ns@old@maketitle} 63 | \endinput 64 | %% 65 | %% End of file `nshyper.sty'. 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Eloquent-JavaScript", 3 | "license": "CC BY-NC 3.0", 4 | "version": "0.1.0", 5 | "author": "Marijn Haverbeke ", 6 | "description": "Sources for the book Eloquent JavaScript", 7 | "repository": { 8 | "type": "git", 9 | "url": "git://github.com/marijnh/Eloquent-JavaScript.git" 10 | }, 11 | "dependencies": { 12 | "acorn": "0.3", 13 | "codemirror": "4", 14 | "jszip": "^2.5.0", 15 | "uglify-js": "2" 16 | }, 17 | "devDependencies": { 18 | "contextify": "kkoopa/contextify", 19 | "jsdom": "0.10", 20 | "canvas": "git://github.com/learnboost/node-canvas", 21 | "promise": "6", 22 | "jszip": "2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /pdf/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hectorip/Eloquent-JavaScript-es/47ba5dc05a15451a8c96d2107c103b82124e24c9/pdf/.DS_Store -------------------------------------------------------------------------------- /pdf/book.tex: -------------------------------------------------------------------------------- 1 | \documentclass[13pt,oneside]{scrbook} 2 | \usepackage{natbib} 3 | \usepackage{charter} 4 | \usepackage{inconsolata} 5 | \usepackage{hyperref} 6 | \usepackage{bookmark} 7 | \usepackage{fontspec} 8 | \usepackage{listings} 9 | \usepackage{makeidx} 10 | 11 | % epigraph is used to style chapter quotes 12 | \usepackage{epigraph} 13 | \setlength{\epigraphwidth}{.8\textwidth} 14 | \setlength{\epigraphrule}{0pt} 15 | 16 | \lstset{basicstyle=\ttfamily,xleftmargin=0.8em,breaklines=true,lineskip=-0.2em,aboveskip=0.8em,belowskip=0.8em} 17 | \date{} 18 | 19 | \makeatletter 20 | \setcounter{secnumdepth}{0} 21 | \setcounter{tocdepth}{1} 22 | \setmonofont[Scale=0.8]{Inconsolata} 23 | \pagestyle{plain} 24 | \makeatother 25 | 26 | \usepackage{newunicodechar} 27 | \newunicodechar{→}{{\tiny$\rightarrow$\normalsize}} 28 | \newunicodechar{←}{$\leftarrow$} 29 | \newunicodechar{“}{``} 30 | \newunicodechar{”}{''} 31 | \newunicodechar{’}{'} 32 | \newunicodechar{π}{$\pi$} 33 | \newunicodechar{½}{$\frac{1}{2}$} 34 | \newunicodechar{⅓}{$\frac{1}{3}$} 35 | \newunicodechar{¼}{$\frac{1}{4}$} 36 | \newunicodechar{…}{...} 37 | \newunicodechar{×}{$\times$} 38 | \newunicodechar{β}{\ss} 39 | \newunicodechar{ϕ}{$\varphi$} 40 | \newunicodechar{≈}{$\approx$} 41 | \newunicodechar{—}{---} 42 | 43 | \graphicspath{ {../} } 44 | \makeindex 45 | 46 | \begin{document} 47 | 48 | \author{Marijn Haverbeke} 49 | 50 | \title{Eloquent JavaScript} 51 | 52 | \subtitle{A Modern Introduction to Programming} 53 | 54 | \maketitle 55 | 56 | \frontmatter 57 | 58 | \noindent Copyright \textcopyright{} 2014 by Marijn Haverbeke 59 | 60 | \vskip 1em 61 | 62 | \noindent This work is licensed under a Creative Commons 63 | attribution-noncommercial license 64 | (\url{http://creativecommons.org/licenses/by-nc/3.0/}). All code in 65 | the book may also be considered licensed under an MIT license 66 | (\url{http://opensource.org/licenses/MIT}). 67 | 68 | The illustrations are contributed by various artists: Cover by 69 | Wasif Hyder. Computer (introduction) and unicycle people (Chapter 70 | 21) by Max Xiantu. Sea of bits (Chapter 1) and weresquirrel (Chapter 71 | 4) by Margarita Martínez and José Menor. Octopuses (Chapter 2 and 4) 72 | by Jim Tierney. Object with on/off switch (Chapter 6) by Dyle 73 | MacGregor. Regular expression diagrams in Chapter 9 generated 74 | with \href{http://regexper.com}{regexper.com} by Jeff 75 | Avallone. Game concept for Chapter 15 76 | by \href{http://lessmilk.com}{Thomas Palef}. Pixel art in 77 | Chapter 16 by Antonio Perdomo Pastor. 78 | 79 | The second edition of Eloquent JavaScript was made possible 80 | by \href{http://eloquentjavascript.net/backers.html}{454 financial backers}. 81 | 82 | \vskip 1em 83 | 84 | \noindent You can buy a print version of this book, with an extra 85 | bonus chapter included, printed by No Starch Press at 86 | \url{http://www.amazon.com/gp/product/1593275846/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1593275846&linkCode=as2&tag=marijhaver-20&linkId=VPXXXSRYC5COG5R5}. 87 | 88 | \tableofcontents 89 | 90 | \mainmatter 91 | 92 | \input{00_intro.tex} 93 | 94 | \input{01_values.tex} 95 | 96 | \input{02_program_structure.tex} 97 | 98 | \input{03_functions.tex} 99 | 100 | \input{04_data.tex} 101 | 102 | \input{05_higher_order.tex} 103 | 104 | \input{06_object.tex} 105 | 106 | \input{07_elife.tex} 107 | 108 | \input{08_error.tex} 109 | 110 | \input{09_regexp.tex} 111 | 112 | \input{10_modules.tex} 113 | 114 | \input{11_language.tex} 115 | 116 | \input{12_browser.tex} 117 | 118 | \input{13_dom.tex} 119 | 120 | \input{14_event.tex} 121 | 122 | \input{15_game.tex} 123 | 124 | \input{16_canvas.tex} 125 | 126 | \input{17_http.tex} 127 | 128 | \input{18_forms.tex} 129 | 130 | \input{19_paint.tex} 131 | 132 | \input{20_node.tex} 133 | 134 | \input{21_skillsharing.tex} 135 | 136 | \input{hints.tex} 137 | 138 | \backmatter 139 | 140 | \printindex 141 | 142 | \end{document} 143 | -------------------------------------------------------------------------------- /pdf/build.sh: -------------------------------------------------------------------------------- 1 | xelatex $1.tex 2 | xelatex $1.tex 3 | makeindex -o $1.ind $1.idx 4 | makeindex -o $1.ind $1.idx 5 | xelatex $1.tex 6 | while ( grep -q '^LaTeX Warning: Label(s) may have changed' $1.log) \ 7 | do xelatex $1.tex; done 8 | --------------------------------------------------------------------------------