Repository: coffeescript-cookbook/coffeescript-cookbook.github.io Branch: master Commit: 53fc0026d147 Files: 147 Total size: 334.9 KB Directory structure: gitextract_ml6e9x2u/ ├── .gitignore ├── .ruby-gemset ├── .ruby-version ├── Gemfile ├── LICENSE-CC-BY.textile ├── Procfile ├── README.md ├── _config.yml ├── _data/ │ └── chapters.yml ├── _layouts/ │ ├── chapter.html │ ├── default.html │ └── recipe.html ├── authors-guide.md ├── authors.md ├── chapters/ │ ├── ajax/ │ │ ├── ajax_request_without_jquery.md │ │ └── index.html │ ├── arrays/ │ │ ├── check-type-is-array.md │ │ ├── concatenating-arrays.md │ │ ├── creating-a-dictionary-object-from-an-array.md │ │ ├── creating-a-string-from-an-array.md │ │ ├── define-ranges.md │ │ ├── filtering-arrays.md │ │ ├── index.html │ │ ├── list-comprehensions.md │ │ ├── mapping-arrays.md │ │ ├── max-array-value.md │ │ ├── reducing-arrays.md │ │ ├── removing-duplicate-elements-from-arrays.md │ │ ├── reversing-arrays.md │ │ ├── shuffling-array-elements.md │ │ ├── testing-every-element.md │ │ ├── using-arrays-to-swap-variables.md │ │ ├── where-for-arrays-of-objects.md │ │ └── zip-function.md │ ├── classes_and_objects/ │ │ ├── chaining.md │ │ ├── class-methods-and-instance-methods.md │ │ ├── class-variables-and-instance-variables.md │ │ ├── cloning.md │ │ ├── index.html │ │ ├── mixins.md │ │ ├── object-literal.md │ │ └── type-function.md │ ├── databases/ │ │ ├── index.html │ │ ├── mongodb.md │ │ └── sqlite.md │ ├── dates_and_times/ │ │ ├── date-of-easter.md │ │ ├── date-of-thanksgiving.md │ │ ├── days-between-two-dates.md │ │ ├── finding-last-day-of-the-month.md │ │ ├── finding-last-or-next-month.md │ │ ├── index.html │ │ └── moon-phase-for-date.md │ ├── design_patterns/ │ │ ├── adapter.md │ │ ├── bridge.md │ │ ├── builder.md │ │ ├── command.md │ │ ├── decorator.md │ │ ├── factory_method.md │ │ ├── index.html │ │ ├── interpreter.md │ │ ├── memento.md │ │ ├── observer.md │ │ ├── singleton.md │ │ ├── strategy.md │ │ └── template_method.md │ ├── functions/ │ │ ├── debounce.md │ │ ├── index.html │ │ ├── parentheses.md │ │ ├── recursion.md │ │ └── splat_arguments.md │ ├── index.html │ ├── jquery/ │ │ ├── ajax.md │ │ ├── callback-bindings-jquery.md │ │ ├── index.html │ │ └── plugin.md │ ├── math/ │ │ ├── constants.md │ │ ├── fast-fibonacci.md │ │ ├── fast-inv-square.md │ │ ├── generating-predictable-random-numbers.md │ │ ├── generating-random-numbers.md │ │ ├── index.html │ │ ├── radians-degrees.md │ │ ├── random-integer.md │ │ └── working-with-exponents-and-logarithms.md │ ├── metaprogramming/ │ │ ├── detecting-and-replacing-functions.md │ │ ├── extending-built-in-objects.md │ │ └── index.html │ ├── networking/ │ │ ├── basic-client.md │ │ ├── basic-http-client.md │ │ ├── basic-http-server.md │ │ ├── basic-server.md │ │ ├── bi-directional-client.md │ │ ├── bi-directional-server.md │ │ └── index.html │ ├── regular_expressions/ │ │ ├── heregexes.md │ │ ├── index.html │ │ ├── replacing-html-tags-with-html-named-entities.md │ │ ├── replacing-substrings.md │ │ └── searching-for-substrings.md │ ├── strings/ │ │ ├── capitalizing-words.md │ │ ├── finding-substrings.md │ │ ├── generating-a-unique-id.md │ │ ├── index.html │ │ ├── interpolation.md │ │ ├── lowercasing-a-string.md │ │ ├── matching-strings.md │ │ ├── repeating.md │ │ ├── replacing-sub-strings.md │ │ ├── splitting-a-string.md │ │ ├── trimming-whitespace-from-a-string.md │ │ └── uppercasing-a-string.md │ ├── syntax/ │ │ ├── code_reuse_on_client_and_server.md │ │ ├── comparing_ranges.md │ │ ├── embedding_javascript.md │ │ ├── for_loops.md │ │ └── index.html │ └── testing/ │ ├── index.html │ ├── testing_with_jasmine.md │ └── testing_with_nodeunit.md ├── contributing.md ├── css/ │ ├── autumn.css │ ├── borland.css │ ├── bw.css │ ├── colorful.css │ ├── default.css │ ├── emacs.css │ ├── friendly.css │ ├── fruity.css │ ├── manni.css │ ├── murphy.css │ ├── native.css │ ├── pastie.css │ ├── perldoc.css │ ├── solarized-light.css │ ├── style.css │ ├── tango.css │ ├── trac.css │ ├── vim.css │ └── vs.css ├── designers-guide.md ├── developers-guide.md ├── index.html ├── js/ │ └── scripts.js ├── license.md ├── recipe-template.md ├── terms-of-use.md └── wanted-recipes.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ .rvmrc _site *.swp *.swo .DS_Store # IntelliJ IDEA / WebStorm /.idea/ /*.iml ================================================ FILE: .ruby-gemset ================================================ coffeescript-cookbook ================================================ FILE: .ruby-version ================================================ 1.9.3 ================================================ FILE: Gemfile ================================================ source 'https://rubygems.org' group :development do gem "github-pages" gem "tzinfo-data" gem "foreman" gem "serve" end ================================================ FILE: LICENSE-CC-BY.textile ================================================ --- layout: default title: "Creative Commons License: Attribution 3.0 Unported (CC BY 3.0)" --- License THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. 1. Definitions "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. "Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. "Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. "You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. "Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. 2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. 3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; to Distribute and Publicly Perform the Work including as incorporated in Collections; and, to Distribute and Publicly Perform Adaptations. For the avoidance of doubt: Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. 4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(b), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(b), as requested. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4 (b) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. 5. Representations, Warranties and Disclaimer UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. Termination This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. 8. Miscellaneous Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. ================================================ FILE: Procfile ================================================ jekyll: bundle exec jekyll build --watch serve: bundle exec serve 4000 development _site ================================================ FILE: README.md ================================================ CoffeeScript Cookbook ============= CoffeeScript Cookbook is a community run website for the CoffeeScript language. We want you to write recipes and make the site better! Contributing ----------- You can find details about contributing [on the site](http://coffeescript-cookbook.github.io/contributing). For now here is a simple formula: 1. Fork the repository at [GitHub](http://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com) 2. Do awesomeness! 3. Send a pull request to [coffeescript-cookbook](http://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com) 4. If we merge your pull request, you get commit access. BAM. Go back to step 2 and stay there as long as you want. Here are some relevant links from the site. * [Contributing](http://coffeescript-cookbook.github.io/contributing) * [Recipe Template](http://coffeescript-cookbook.github.io/recipe-template) * [Author's Guide](http://coffeescript-cookbook.github.io/authors-guide) * [Developer's Guide](http://coffeescript-cookbook.github.io/developers-guide) * [Designer's Guide](http://coffeescript-cookbook.github.io/designers-guide) Jekyll ------ CoffeeScript Cookbook is currently implemented as a jekyll site. Jekyll is awesome. * [Site](http://jekyllrb.com/) * [Code](https://github.com/mojombo/jekyll) * [Wiki](https://github.com/mojombo/jekyll/wiki) License ------- This site and all contributions are [licensed](http://coffeescript-cookbook.github.io/LICENSE-CC-BY) under the Creative Commons Attribution 3.0 Unported (CC BY 3.0) license. By submitting information to this site you agree to grant this license to all users of the site, and that your editing of the authors page constitutes satisfactory attribution. ================================================ FILE: _config.yml ================================================ baseurl: '' safe: true highlighter: pygments lsi: false markdown: redcarpet exclude: - README.md - CNAME - TODO.md - script - Procfile - Gemfile - Gemfile.lock ================================================ FILE: _data/chapters.yml ================================================ --- - Syntax - Classes and Objects - Strings - Arrays - Dates and Times - Math - Functions - Metaprogramming - jQuery - Ajax - Regular Expressions - Networking - Design Patterns - Databases - Testing ================================================ FILE: _layouts/chapter.html ================================================ CoffeeScript Cookbook » {{ page.title }}

óCoffeeScript Cookbook

{{ page.title }}

{{ content }}
================================================ FILE: _layouts/default.html ================================================ CoffeeScript Cookbook » {{ page.title }}

óCoffeeScript Cookbook

{{ content }}
================================================ FILE: _layouts/recipe.html ================================================ CoffeeScript Cookbook » {{ page.title }}

óCoffeeScript Cookbook

{{ page.title }}

{{ content }}
================================================ FILE: authors-guide.md ================================================ --- layout: default title: Author's Guide --- # Author's Guide ## tl;dr: Look at Other Recipes, and Blend In Look at the source of other recipe pages and follow that page structure. Start with the [Developer's Guide]({{ site.baseurl }}/developers-guide) to get a test version of the cookbook up and running on your machine, and get to work! ## General Guidelines * Feel free to add new pages, or even chapters. Just keep them organized. _(See "How to Add a Chapter" below)_ * Try to write well-styled, idiomatic CoffeeScript. * Try to stay within the Problem/Solution/Discussion format. If you can't think of a good problem for your recipe... hang onto it for a while. * Sharing code that you think might only be used rarely is fine. Sharing esoteric code tricks is less so. There's a difference between sharing a translator for an obscure format and sharing a weird bit-shifting trick that does fast but inaccurate multiplication. * Don't forget to add your name to the authors page! ## Use Cookbook Problem/Solution/Discussion Format A typical cookbook page will have three sections (four if you count the title): * **Problem** A one- or two-sentence description of the problem, such as "You want to access parts of a string" or "You want to format a floating point number as currency, with a leading dollar sign, two digits of precision and comma-separated triples." Where possible, phrase the problem as though speaking directly to the reader: "You want to...", "You have an X but you want a Y", etc. * **Solution** State the solution as briefly as possible, ideally as a single sentence that identifies the strategy you would use, and give example code. It's tempting to explain the solution, but don't do it yet. Remember that the reader will look this solution up many times after the first time, and that they will be looking for a quick reference each time. You're going to explain the solution in the **Discussion**, and the first time reader will read your solution, think about it, and then proceed to the discussion if necessary. For example, for "accessing parts of a string", a good Solution sentence might be "Use CoffeeScript's array range subscripts, or JavaScript's slice function." * **Discussion** Here you should explain why your solution works, or give further examples such as edge cases. If your solution can break on some edge cases, be sure to note them here. For example, if a percentage function crashes when given a zero, you could note this in the discussion and give a workaround. NOTE: If your solution has really dangerous edge cases, so dangerous that you would include them in the solution, please consider whether your recipe should be included at all. Remember, this Cookbook is for good code! ## Copyright Issues Do not post code that is copyrighted by another user, unless you have permission to use that code AND to relicense that code under the [CC BY 3.0]({{ site.baseurl }}/license) license. If you DO have permission and the author would like credit, please add them to the [authors]({{ site.baseurl }}/authors) page. Also, just a stylistic note, please do not yank code directly from [http://coffeescript.org/](http://coffeescript.org/) and post it with little or no discussion. The CoffeeScript Cookbook is not affiliated with them. We think they're awesome and want them to like us, too! Make sure that anything taken from [http://coffeescript.org/](http://coffeescript.org/) is permissible use and that it stands alone as a valid recipe. If the recipe is too terse, consider adding more examples and discussion. ## Tag the page with Jekyll frontmatter ...that's a lot of fancy words that mean "don't forget to put the layout, chapter and page title at the top of the file in a YAML block". For example, the string interpolation page begins with {% highlight text %} --- layout: recipe title: String Interpolation chapter: Strings --- {% endhighlight %} ## Use Liquid highlighting templates Turn on syntax highlighting for CoffeeScript with `highlight coffeescript`. test2 {% highlight coffeescript %} # Calculate the square of a number square = (x) -> x * x square(16) # => 256 {% endhighlight %} This produces the following: {% highlight coffeescript %} # Calculate the square of a number square = (x) -> x * x square(16) # => 256 {% endhighlight %} ## Include Output After writing an important expression, show the reader what the output would be by adding it with a `# =>` on the following line. Once we get automated script testing working up in this joint, we'll actually load up your recipe snippets and evaluate its expressions against the output comment you wrote. (In other words, `# =>` tells the reader what the output will be, but it tells automated checker what the assertEquals should be) {% highlight coffeescript %} # right [1,2,3].map (x) -> x * 2 # => [ 2, 4, 6 ] # very wrong! [1,2,3].map (x) -> x * 2 # right; only add for important/results statements evens = x for x in [0..10] by 2 evens.some (x) -> x == 6 # => true # less wrong; may require tweaking the automatic checker [1,2,3].map (x) -> x * 2 # => [ 2, 4, 6 ] # less wrong (possibly not wrong at all--the output merely does not match what the coffee interpreter renders) [1,2,3].map (x) -> x * 2 # => [2,4,6] {% endhighlight %} Not all snippets evaluate to something useful. For example, array.forEach has side effects but does not return anything. Also, the console.log() and alert() commands are also side-effect-only commands that return nothing. When possible, try to have your snippets evaluate to something inspectable, and leave displaying to the console or browser out of it. (Unless your snippet is _about_ displaying to the console or browser.) When in doubt about what output to show, try evaluating your snippet in the coffee interpreter and see what IT thinks. Ideally your output should match, or at least be machine-comparable. # Grindy HOWTOs ## How to Add a Recipe Create a new markdown page (or copy the [Recipe Template]({{ site.baseurl }}/recipe-template). The filename should be about the problem, e.g. `finding-last-day-of-the-month.md` or `reversing-arrays.md`. In your file, start with the following template: {% highlight text %} --- layout: recipe title: Title of The Recipe chapter: Chapter Name --- ## Problem You have a problem. ## Solution Do this about it. ## Discussion Here's why. {% endhighlight %} One fussy little bit, the chapter name should match the directory the chapter is in, otherwise the page won't render correctly. For example, if you're writing a recipe for arrays, make sure the chapter is "Arrays", not "arrays" or "aray" ...or especially not "Chapter Name". ## How to Add a Chapter * Open chapters/index.html and your chapter's name to the yaml list of chapters. * cd into chapters/ and create the directory for the chapter name. Downcase the name and replace spaces with underscores. * add an index.html file that uses `layout: chapter`. For convenience, just copy the index.html from another chapter and update the yaml frontmatter to reflect the name of your new chapter. For example, to add a chapter called "Dates and Times", you would add it to the chapters array: {% highlight yaml %} chapters: - Syntax - Objects - Arrays - Dates and Times {% endhighlight %} ...and then create that chapter in the file system: {% highlight bash %} cd chapters mkdir dates_and_times {% endhighlight %} Now create a new page in that chapter (remember to add its YAML front matter) and once jekyll regenerates the chapter index, your new page should appear. # FAQ ## I Have a Weird Recipe. Should I Share It? Maybe! The real question is, is it really useful, or is it just clever? If you have a formatter for a weird Albanian GIS format, that's a real problem that almost nobody would ever have -- but when somebody DOES have that problem, they REALLY need a solution. If you have a bit shifting trick that can swap two numbers using three xor-equals operations, that's a really clever solution but it's not very good CoffeeScript. (For one thing, `x ^= y ^= x ^= y` is not idiomatic, while `[x,y]=[y,x]` is. For another, there's a bug in that code. And once you fix it, there's another bug caused by extrapolating this register trick to a reference-based language where -- look, it's just a bad idea, okay?) If you have a cool but weird recipe, ask yourself if a reader would genuinely find it useful. Here are two very good questions to consider: * Does it really solve a problem that an actual person might have? * If somebody really does have that problem, would your recipe really be the best solution? If the answer to either question is no, you might have some code that is a "clever solution in search of a problem". If in doubt, ask. ## What If My Recipe is Inefficient/Too Big/Too Slow? If it solves a problem to which the alternative is to _not_ solve the problem, share it. Let the reader decide if they want to use it. Sometimes we want tight efficient code, other times we want a robust feature set. If the code has abysmal performance characteristics, be sure to warn the reader in the Discussion. ## Can I Edit An Existing Recipe? Yes. Please improve anything and everything! Be sure to test your changes and make sure that your solution really is better. ## I Have a Really Efficient Solution, But It's Not As Readable As the Existing Recipe. Should I Add It? See the "Weird Recipe" note above. Do real people in the real world ever hit the performance constraint? If so, then by all means, add your strategy to the existing solution, and be sure to explain why your solution is not idiomatic. If a reader really has that problem, they'll be glad for the extra options. ## I Have A Problem/Solution, But It's Basically Just JavaScript. Should I Add It? Yes! CoffeeScript compiles to JavaScript, and that means that some of its functionality comes straight from JavaScript. (For example, see [Reversing Arrays]({{ site.baseurl }}/chapters/arrays/reversing-arrays).) But if you're programming in CoffeeScript and you need to reverse an array, this Cookbook should stand ready to tell you it's available to you in CoffeeScript -- even if it's just a straight call into a JavaScript library. ## I Found a Typo. Is That Enough of a Fix? Does That Count? Absolutely! ================================================ FILE: authors.md ================================================ --- layout: default title: Authors --- # Authors The following people are totally rad and awesome because they have contributed recipes! * David Brady *ratgeyser@gmail.com* * John Ford *jwford@gmail.com* * Steven Reid *steven @ reidnorthwest . com* * David Moulton *dave@themoultons.net* * Sebastian Slomski *sebastian@simple-systems.org* * Aaron Weinberger *aw9994@cs.ship.edu* * James C. Holder *coffeescriptcookbook.com@thirdtruck.org* * Jason Giedymin *jasong@apache.org* * Phil Cohen *github@phlippers.net* * João Moreno *coffeecb @joaomoreno .com* * Jeff Pickhardt *pickhardt (at) gmail (dot) com* * Frederic Hemberger * Mike Hatfield *oakraven13@gmail.com* * [Anton Rissanen](http://github.com/antris) *hello@anton.fi* * Calum Robertson *http://github.com/randusr836* * Jake Burkhead *https://github.com/jlburkhead* * [Alex Johnson](https://github.com/nonsensery) * ...You! What are you waiting for? Check out the [contributing]({{ site.baseurl }}/contributing) section and get cracking! # Developers *The following people are amazingly rad and awesome because they have helped fix the code for the site!* * David Brady *ratgeyser@gmail.com* * Mike Moore *mike@blowmage.com* * Peter Hellberg *peter@c7.se* * Jamie Gaskins *jgaskins@gmail.com* * Sami Pussinen *me@samipussinen.com* * [Devin Weaver (@sukima)](https://github.com/sukima) * ...You! What are you waiting for? Check out the [contributing]({{ site.baseurl }}/contributing) section and get cracking! # Designers The following people are astonishingly rad and awesome because they did great design for the site! * [Amsul](http://github.com/amsul) reach@amsul.ca * ...You! Check out the [contributing]({{ site.baseurl }}/contributing) section and get cracking! ================================================ FILE: chapters/ajax/ajax_request_without_jquery.md ================================================ --- layout: recipe title: Ajax Request Without jQuery chapter: Ajax --- ## Problem You want to load data from your server via AJAX without using the jQuery library. ## Solution You will use the native XMLHttpRequest object. Let's set up a simple test HTML page with a button. {% highlight html %} XMLHttpRequest Tester

XMLHttpRequest Tester

{% endhighlight %} When the button is clicked, we want to send an Ajax request to the server to retrieve some data. For this sample, we have a small JSON file. {% highlight javascript %} // data.json { message: "Hello World" } {% endhighlight %} Next, create the CoffeeScript file to hold the page logic. The code in this file creates a function to be called when the Load Data button is clicked. {% highlight coffeescript linenos %} # XMLHttpRequest.coffee loadDataFromServer = -> req = new XMLHttpRequest() req.addEventListener 'readystatechange', -> if req.readyState is 4 # ReadyState Complete successResultCodes = [200, 304] if req.status in successResultCodes data = eval '(' + req.responseText + ')' console.log 'data message: ', data.message else console.log 'Error loading data...' req.open 'GET', 'data.json', false req.send() loadDataButton = document.getElementById 'loadDataButton' loadDataButton.addEventListener 'click', loadDataFromServer, false {% endhighlight %} ## Discussion In the above code we grab a handle to the button in our HTML (line 16) and add a *click* event listener (line 17). In our event listener, we define our callback function as loadDataFromServer. We define our loadDataFromServer callback beginning on line 2. We create a XMLHttpRequest request object (line 3) and add a *readystatechange* event handler. This fires whenever the request's readyState changes. In the event handler we check to see if the readyState = 4, indicating the request has completed. Then, we check the request status value. Both 200 or 304 represent a successful request. Anything else represents an error condition. If the request was indeed successful, we eval the JSON returned from the server and assign it to a data variable. At this point, we can use the returned data in any way we need to. The last thing we need to do is actually make our request. Line 13 opens a 'GET' request to retrieve the data.json file. Line 14 sends our request to the server. ## Older Browser Support If your application needs to target older versions of Internet Explorer, you will need to ensure the XMLHttpRequest object exists. You can do this by including this code before creating the XMLHttpRequest instance. {% highlight coffeescript %} if (typeof @XMLHttpRequest == "undefined") console.log 'XMLHttpRequest is undefined' @XMLHttpRequest = -> try return new ActiveXObject("Msxml2.XMLHTTP.6.0") catch error try return new ActiveXObject("Msxml2.XMLHTTP.3.0") catch error try return new ActiveXObject("Microsoft.XMLHTTP") catch error throw new Error("This browser does not support XMLHttpRequest.") {% endhighlight %} This code ensures the XMLHttpRequest object is available in the global namespace. ================================================ FILE: chapters/ajax/index.html ================================================ --- layout: chapter title: Ajax chapter: Ajax --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/arrays/check-type-is-array.md ================================================ --- layout: recipe title: Check if type of value is an Array chapter: Arrays --- ## Problem You want to check if a value is an `Array`. {% highlight coffeescript %} myArray = [] console.log typeof myArray // outputs 'object' {% endhighlight %} The `typeof` operator gives a faulty output for arrays. ## Solution Use the following code: {% highlight coffeescript %} typeIsArray = Array.isArray || ( value ) -> return {}.toString.call( value ) is '[object Array]' {% endhighlight %} To use this, just call `typeIsArray` as such: {% highlight coffeescript %} myArray = [] typeIsArray myArray // outputs true {% endhighlight %} ## Discussion The method above has been adopted from "the Miller Device". An alternative is to use Douglas Crockford's snippet: {% highlight coffeescript %} typeIsArray = ( value ) -> value and typeof value is 'object' and value instanceof Array and typeof value.length is 'number' and typeof value.splice is 'function' and not ( value.propertyIsEnumerable 'length' ) {% endhighlight %} ================================================ FILE: chapters/arrays/concatenating-arrays.md ================================================ --- layout: recipe title: Concatenating Arrays chapter: Arrays --- ## Problem You want to join two arrays together. ## Solution There are two standard options for concatenating arrays in JavaScript. The first is to use JavaScript's Array `concat()` method: {% highlight coffeescript %} array1 = [1, 2, 3] array2 = [4, 5, 6] array3 = array1.concat array2 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} Note that `array1` is _not_ modified by the operation. The concatenated array is returned as a new object. If you want to merge two arrays without creating a new object, you can use the following technique: {% highlight coffeescript %} array1 = [1, 2, 3] array2 = [4, 5, 6] Array::push.apply array1, array2 array1 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} In the example above, the `Array.prototype.push.apply(a, b)` approach modifies `array1` in place without creating a new array object. We can simplify the pattern above using CoffeeScript by creating a new `merge()` method for Arrays. {% highlight coffeescript %} Array::merge = (other) -> Array::push.apply @, other array1 = [1, 2, 3] array2 = [4, 5, 6] array1.merge array2 array1 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} Alternatively, we can pass a CoffeeScript splat (`array2...`) directly into `push()`, avoiding the Array prototype. {% highlight coffeescript %} array1 = [1, 2, 3] array2 = [4, 5, 6] array1.push array2... array1 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} A more idiomatic approach is to use the splat operator (`...`) directly in an array literal. This can be used to concatenate any number of arrays. {% highlight coffeescript %} array1 = [1, 2, 3] array2 = [4, 5, 6] array3 = [array1..., array2...] array3 # => [1, 2, 3, 4, 5, 6] {% endhighlight %} ## Discussion CoffeeScript lacks a special syntax for joining arrays, but `concat()` and `push()` are standard JavaScript methods. ================================================ FILE: chapters/arrays/creating-a-dictionary-object-from-an-array.md ================================================ --- layout: recipe title: Creating a dictionary Object from an Array chapter: Arrays --- ## Problem You have an Array of Objects, such as: {% highlight coffeescript %} cats = [ { name: "Bubbles" age: 1 }, { name: "Sparkle" favoriteFood: "tuna" } ] {% endhighlight %} But you want to access it as a dictionary by key, like `cats["Bubbles"]`. ## Solution You need to convert your array into an Object. Use reduce for this. {% highlight coffeescript %} # key = The key by which to index the dictionary Array::toDict = (key) -> @reduce ((dict, obj) -> dict[ obj[key] ] = obj if obj[key]?; return dict), {} {% endhighlight %} To use this: {% highlight coffeescript %} catsDict = cats.toDict('name') catsDict["Bubbles"] # => { age: 1, name: "Bubbles" } {% endhighlight %} ## Discussion Alternatively, you can use an Array comprehension: {% highlight coffeescript %} Array::toDict = (key) -> dict = {} dict[obj[key]] = obj for obj in this when obj[key]? dict {% endhighlight %} If you use Underscore.js, you can create a mixin: {% highlight coffeescript %} _.mixin toDict: (arr, key) -> throw new Error('_.toDict takes an Array') unless _.isArray arr _.reduce arr, ((dict, obj) -> dict[ obj[key] ] = obj if obj[key]?; return dict), {} catsDict = _.toDict(cats, 'name') catsDict["Sparkle"] # => { favoriteFood: "tuna", name: "Sparkle" } {% endhighlight %} ================================================ FILE: chapters/arrays/creating-a-string-from-an-array.md ================================================ --- layout: recipe title: Creating a String from an Array chapter: Arrays --- ## Problem You want to create a string from an array. ## Solution Use JavaScript's Array toString() method: {% highlight coffeescript %} ["one", "two", "three"].toString() # => 'one,two,three' {% endhighlight %} ## Discussion `toString()` is a standard JavaScript method. Don't forget the parentheses. ================================================ FILE: chapters/arrays/define-ranges.md ================================================ --- layout: recipe title: Define Ranges Array chapter: Arrays --- ## Problem You want to define a range in an array. ## Solution There are two ways to define a range of array elements in CoffeeScript. {% highlight coffeescript %} # inclusive myArray = [1..10] # => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] {% endhighlight %} {% highlight coffeescript %} # exclusive myArray = [1...10] # => [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ] {% endhighlight %} We can also reverse the range of element by writing it this way. {% highlight coffeescript %} myLargeArray = [10..1] # => [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ] {% endhighlight %} {% highlight coffeescript %} myLargeArray = [10...1] # => [ 10, 9, 8, 7, 6, 5, 4, 3, 2 ] {% endhighlight %} ## Discussion Inclusive ranges are defined by the '..' operator and include the last value. Exclusive ranges are defined by '...', and always omit the last value. ================================================ FILE: chapters/arrays/filtering-arrays.md ================================================ --- layout: recipe title: Filtering Arrays chapter: Arrays --- ## Problem You want to be able to filter arrays based on a boolean condition. ## Solution Use Array.filter (ECMAScript 5): {% highlight coffeescript %} array = [1..10] array.filter (x) -> x > 5 # => [6,7,8,9,10] {% endhighlight %} In pre-EC5 implementations, extend the Array prototype to add a filter function which will take a callback and perform a comprehension over itself, collecting all elements for which the callback is true. Be sure to check if the function is already implemented before overwriting it: {% highlight coffeescript %} # Extending Array's prototype unless Array::filter Array::filter = (callback) -> element for element in this when callback(element) array = [1..10] # Filter odd elements filtered_array = array.filter (x) -> x % 2 == 0 # => [2,4,6,8,10] # Filter elements less than or equal to 5: gt_five = (x) -> x > 5 filtered_array = array.filter gt_five # => [6,7,8,9,10] {% endhighlight %} ## Discussion This is similar to using ruby's Array#select method. ================================================ FILE: chapters/arrays/index.html ================================================ --- layout: chapter title: Arrays chapter: Arrays --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/arrays/list-comprehensions.md ================================================ --- layout: recipe title: List Comprehensions chapter: Arrays --- ## Problem You have an array of objects and want to map them to another array, similar to Python's list comprehensions. ## Solution Use a list comprehension, but don't forget about [mapping arrays]({{ site.baseurl }}/chapters/arrays/mapping-arrays). {% highlight coffeescript %} electric_mayhem = [ { name: "Doctor Teeth", instrument: "piano" }, { name: "Janice", instrument: "lead guitar" }, { name: "Sgt. Floyd Pepper", instrument: "bass" }, { name: "Zoot", instrument: "sax" }, { name: "Lips", instrument: "trumpet" }, { name: "Animal", instrument: "drums" } ] names = (muppet.name for muppet in electric_mayhem) # => [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoot', 'Lips', 'Animal' ] {% endhighlight %} ## Discussion Because CoffeeScript directly support list comprehensions, they work pretty much as advertised wherever you would use one in Python. For simple mappings, list comprehensions are much more readable. For complicated transformations or for chained mappings, [mapping arrays]({{ site.baseurl }}/chapters/arrays/mapping-arrays) might be more elegant. ================================================ FILE: chapters/arrays/mapping-arrays.md ================================================ --- layout: recipe title: Mapping Arrays chapter: Arrays --- ## Problem You have an array of objects and want to map them to another array, similar to Ruby's map. ## Solution Use map() with an anonymous function, but don't forget about list comprehensions. {% highlight coffeescript %} electric_mayhem = [ { name: "Doctor Teeth", instrument: "piano" }, { name: "Janice", instrument: "lead guitar" }, { name: "Sgt. Floyd Pepper", instrument: "bass" }, { name: "Zoot", instrument: "sax" }, { name: "Lips", instrument: "trumpet" }, { name: "Animal", instrument: "drums" } ] names = electric_mayhem.map (muppet) -> muppet.name # => [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoot', 'Lips', 'Animal' ] {% endhighlight %} ## Discussion Because CoffeeScript has clean support for anonymous functions, mapping an array in CoffeeScript is nearly as easy as it is in Ruby. Maps are are good way to handle complicated transforms and chained mappings in CoffeeScript. If your transformation is as simple as the one above, however, it may read more cleanly as a list comprehension. ================================================ FILE: chapters/arrays/max-array-value.md ================================================ --- layout: recipe title: Max Array Value chapter: Arrays --- ## Problem You need to find the largest value contained in an array. ## Solution You can use Math.max() JavaScript method along with splats. {% highlight coffeescript %} Math.max [12, 32, 11, 67, 1, 3]... # => 67 {% endhighlight %} Alternatively, it's possible to use ES5 `reduce` method. For backward compatibility with older JavaScript implementations, use the above. {% highlight coffeescript %} # ECMAScript 5 [12,32,11,67,1,3].reduce (a,b) -> Math.max a, b # => 67 {% endhighlight %} ## Discussion `Math.max` compares every argument and returns the largest number from arguments. The ellipsis (`...`) converts every array value into argument which is given to the function. You can also use it with other functions which take variable amount of arguments, such as `console.log`. ================================================ FILE: chapters/arrays/reducing-arrays.md ================================================ --- layout: recipe title: Reducing Arrays chapter: Arrays --- ## Problem You have an array of objects and want to reduce them to a value, similar to Ruby's `reduce()` and `reduceRight()`. ## Solution You can simply use Array's `reduce()` and `reduceRight()` methods along with an anonymous function, keeping the code clean and readable. The reduction may be something simple such as using the `+` operator with numbers or strings. {% highlight coffeescript %} [1,2,3,4].reduce (x,y) -> x + y # => 10 {% endhighlight %} {% highlight coffeescript %} ["words", "of", "bunch", "A"].reduceRight (x, y) -> x + " " + y # => 'A bunch of words' {% endhighlight %} Or it may be something more complex such as aggregating elements from a list into a combined object. {% highlight coffeescript %} people = [ { name: 'alec', age: 10 } { name: 'bert', age: 16 } { name: 'chad', age: 17 } ] people.reduce (x, y) -> x[y.name]= y.age x , {} # => { alec: 10, bert: 16, chad: 17 } {% endhighlight %} ## Discussion Javascript introduced `reduce` and `reduceRight` in version 1.8. Coffeescript provides a natural and simple way to express anonymous functions. Both go together cleanly in the problem of merging a collection's items into a combined result. ================================================ FILE: chapters/arrays/removing-duplicate-elements-from-arrays.md ================================================ --- layout: recipe title: Removing Duplicate Elements from Arrays chapter: Arrays --- ## Problem You want to remove duplicate elements from an array. ## Solution {% highlight coffeescript %} Array::unique = -> output = {} output[@[key]] = @[key] for key in [0...@length] value for key, value of output [1,1,2,2,2,3,4,5,6,6,6,"a","a","b","d","b","c"].unique() # => [ 1, 2, 3, 4, 5, 6, 'a', 'b', 'd', 'c' ] {% endhighlight %} ## Discussion There are many implementations of the `unique` method in JavaScript. This one is based on "The fastest method to find unique items in array" found [here](http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/). **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). ================================================ FILE: chapters/arrays/reversing-arrays.md ================================================ --- layout: recipe title: Reversing Arrays chapter: Arrays --- ## Problem You want to reverse an array. ## Solution Use JavaScript's Array reverse() method: {% highlight coffeescript %} ["one", "two", "three"].reverse() # => ["three", "two", "one"] {% endhighlight %} ## Discussion `reverse()` is a standard JavaScript method. Don't forget the parentheses. ================================================ FILE: chapters/arrays/shuffling-array-elements.md ================================================ --- layout: recipe title: Shuffling Array Elements chapter: Arrays --- ## Problem You want to shuffle the elements in an array. ## Solution The [Fisher-Yates shuffle] is a highly efficient and completely unbiased way to randomize the elements in an array. It's a fairly simple method: Start at the end of the list, and swap the last element with a random element from earlier in the list. Go down one and repeat, until you're at the beginning of the list, with all of the shuffled elements at the end of the list. This [Fisher-Yates shuffle Visualization] may help you understand the algorithm. {% highlight coffeescript %} shuffle = (source) -> # Arrays with < 2 elements do not shuffle well. Instead make it a noop. return source unless source.length >= 2 # From the end of the list to the beginning, pick element `index`. for index in [source.length-1..1] # Choose random element `randomIndex` to the front of `index` to swap with. randomIndex = Math.floor Math.random() * (index + 1) # Swap `randomIndex` with `index`, using destructured assignment [source[index], source[randomIndex]] = [source[randomIndex], source[index]] source shuffle([1..9]) # => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ] {% endhighlight %} [Fisher-Yates shuffle]: http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle [Fisher-Yates Shuffle Visualization]: http://bost.ocks.org/mike/shuffle/ ## Discussion ### The Wrong Way to do it There is a common--but terribly wrong way--to shuffle an array, by sorting by a random number. {% highlight coffeescript %} shuffle = (a) -> a.sort -> 0.5 - Math.random() {% endhighlight %} If you do a sort randomly, it should give you a random order, right? Even [Microsoft used this random-sort algorithm][msftshuffle]. Turns out, [this random-sort algorithm produces biased results][naive], because it only has the illusion of shuffling. Randomly sorting will not result in a neat, tidy shuffle; it will result in a wild mass of inconsistent sorting. [msftshuffle]: http://www.robweir.com/blog/2010/02/microsoft-random-browser-ballot.html [naive]: http://www.codinghorror.com/blog/2007/12/the-danger-of-naivete.html ### Optimizing for speed and space The solution above isn't as fast, or as lean, as it can be. The list comprehension, when transformed into Javascript, is far more complex than it needs to be, and the destructured assignment is far slower than dealing with bare variables. The following code is less idiomatic, and takes up more source-code space... but will compile down smaller and run a bit faster: {% highlight coffeescript %} shuffle = (a) -> i = a.length while --i > 0 j = ~~(Math.random() * (i + 1)) # ~~ is a common optimization for Math.floor t = a[j] a[j] = a[i] a[i] = t a {% endhighlight %} ### Extending Javascript to include this shuffle. The following code adds the shuffle function to the Array prototype, which means that you are able to run it on any array you wish, in a much more direct manner. {% highlight coffeescript %} Array::shuffle ?= -> if @length > 1 then for i in [@length-1..1] j = Math.floor Math.random() * (i + 1) [@[i], @[j]] = [@[j], @[i]] this [1..9].shuffle() # => [ 3, 1, 5, 6, 4, 8, 2, 9, 7 ] {% endhighlight %} **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own][dontown]; [Extending built-in native objects. Evil or not?] [extendevil]). That being said, the code above is really quite safe to add. It only adds `Array::shuffle` if it doesn't exist already, thanks to the existential assignment operator (`?=`). That way, we don't overwrite someone else's, or a native browser method. Also, if you think you'll be using a lot of these utility functions, consider using a utility library, like [Lo-dash](http://lodash.com/)^†. They include a lot of nifty features, like maps and forEach, in a cross-browser, lean, high-performance way. ^† [Underscore](http://underscorejs.org/) is also a good alternative to Lo-dash. [dontown]: http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/ [extendevil]: http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/ ================================================ FILE: chapters/arrays/testing-every-element.md ================================================ --- layout: recipe title: Testing Every Element chapter: Arrays --- ## Problem You want to be able to check that every element in an array meets a particular condition. ## Solution Use Array.every (ECMAScript 5): {% highlight coffeescript %} evens = (x for x in [0..10] by 2) evens.every (x)-> x % 2 == 0 # => true {% endhighlight %} Array.every was added to Mozilla's Javascript 1.6 and made standard with ECMAScript 5. If you to support browsers that do not implement EC5 then check out [`_.all` from underscore.js][underscore]. For a real world example, pretend you have a multiple select list that looks like: {% highlight html %} {% endhighlight %} Now you want to verify that the user selected only numbers. Let's use Array.every: {% highlight coffeescript %} validateNumeric = (item)-> parseFloat(item) == parseInt(item) && !isNaN(item) values = $("#my-select-list").val() values.every validateNumeric {% endhighlight %} ## Discussion This is similar to using ruby's Array#all? method. [underscore]: http://documentcloud.github.com/underscore/ ================================================ FILE: chapters/arrays/using-arrays-to-swap-variables.md ================================================ --- layout: recipe title: Using Arrays to Swap Variables chapter: Arrays --- ## Problem You want to use an array to swap variables. ## Solution Use CoffeeScript's [destructuring assignment](http://jashkenas.github.com/coffee-script/#destructuring) syntax: {% highlight coffeescript %} a = 1 b = 3 [a, b] = [b, a] a # => 3 b # => 1 {% endhighlight %} ## Discussion Destructuring assignment allows swapping two values without the use of a temporary variable. This can be useful when traversing arrays and ensuring iteration only happens over the shortest one: {% highlight coffeescript %} ray1 = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ] ray2 = [ 5, 9, 14, 20 ] intersection = (a, b) -> [a, b] = [b, a] if a.length > b.length value for value in a when value in b intersection ray1, ray2 # => [ 5, 9 ] intersection ray2, ray1 # => [ 5, 9 ] {% endhighlight %} ================================================ FILE: chapters/arrays/where-for-arrays-of-objects.md ================================================ --- layout: recipe title: where for arrays of objects chapter: Arrays --- ## Problem You want to get an array of objects that match your request for some properties You have an Array of Objects, such as: {% highlight coffeescript %} cats = [ { name: "Bubbles" favoriteFood: "mice" age: 1 }, { name: "Sparkle" favoriteFood: "tuna" }, { name: "flyingCat" favoriteFood: "mice" age: 1 } ] {% endhighlight %} You want to filter with some properties, like cats.where({ age: 1}) or cats.where({ age: 1, favoriteFood: "mice"}) ## Solution You can extend Array like this : {% highlight coffeescript %} Array::where = (query) -> return [] if typeof query isnt "object" hit = Object.keys(query).length @filter (item) -> match = 0 for key, val of query match += 1 if item[key] is val if match is hit then true else false cats.where age:1 # => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 },{ name: 'flyingCat', favoriteFood: 'mice', age: 1 } ] cats.where age:1, name: "Bubbles" # => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 } ] cats.where age:1, favoriteFood:"tuna" # => [] {% endhighlight %} ## Discussion This is an exact match. we could make it more flexible with a matcher function : {% highlight coffeescript %} Array::where = (query, matcher = (a,b) -> a is b) -> return [] if typeof query isnt "object" hit = Object.keys(query).length @filter (item) -> match = 0 for key, val of query match += 1 if matcher(item[key], val) if match is hit then true else false cats.where name:"bubbles" # => [] # it's case sensitive cats.where name:"bubbles", (a, b) -> "#{ a }".toLowerCase() is "#{ b }".toLowerCase() # => [ { name: 'Bubbles', favoriteFood: 'mice', age: 1 } ] # now it's case insensitive {% endhighlight %} it's more a method to deal with collection and it could be rename as "find" but popular libraries like underscore or lodash name it "where". ================================================ FILE: chapters/arrays/zip-function.md ================================================ --- layout: recipe title: Python-like Zip Function chapter: Arrays --- ## Problem You want to zip together multiple arrays into an array of arrays, similar to Python's zip function. Python's zip function returns an array of tuples, where each tuple contains the i-th element from each of the argument arrays. ## Solution Use the following CoffeeScript code: {% highlight coffeescript %} # Usage: zip(arr1, arr2, arr3, ...) zip = () -> lengthArray = (arr.length for arr in arguments) length = Math.min(lengthArray...) for i in [0...length] arr[i] for arr in arguments zip([0, 1, 2, 3], [0, -1, -2, -3]) # => [[0, 0], [1, -1], [2, -2], [3, -3]] {% endhighlight %} ================================================ FILE: chapters/classes_and_objects/chaining.md ================================================ --- layout: recipe title: Chaining Calls to an Object chapter: Classes and Objects --- ## Problem You want to call multiple methods on a single object without having to reference that object each time. ## Solution Return the `this` (i.e. `@`) object after every chained method. {% highlight coffeescript %} class CoffeeCup constructor: -> @properties= strength: 'medium' cream: false sugar: false strength: (newStrength) -> @properties.strength = newStrength this cream: (newCream) -> @properties.cream = newCream this sugar: (newSugar) -> @properties.sugar = newSugar this morningCup = new CoffeeCup() morningCup.properties # => { strength: 'medium', cream: false, sugar: false } eveningCup = new CoffeeCup().strength('dark').cream(true).sugar(true) eveningCup.properties # => { strength: 'dark', cream: true, sugar: true } {% endhighlight %} ## Discussion The jQuery library uses a similar approach by returning a selector object from every relevant method, modifying it as subsequent methods tweak the selection: {% highlight coffeescript %} $('p').filter('.topic').first() {% endhighlight %} For your own objects, a touch of metaprogramming can automate the setup process and explicitly state the purpose of returning *this*. {% highlight coffeescript %} addChainedAttributeAccessor = (obj, propertyAttr, attr) -> obj[attr] = (newValues...) -> if newValues.length == 0 obj[propertyAttr][attr] else obj[propertyAttr][attr] = newValues[0] obj class TeaCup constructor: -> @properties= size: 'medium' type: 'black' sugar: false cream: false addChainedAttributeAccessor(this, 'properties', attr) for attr of @properties earlgrey = new TeaCup().size('small').type('Earl Grey').sugar(false) earlgrey.properties # => { size: 'small', type: 'Earl Grey', sugar: false, cream: false } earlgrey.sugar true earlgrey.sugar() # => true {% endhighlight %} ================================================ FILE: chapters/classes_and_objects/class-methods-and-instance-methods.md ================================================ --- layout: recipe title: Class Methods and Instance Methods chapter: Classes and Objects --- ## Problem You want to create class and instance methods. ## Solution ### Class Method {% highlight coffeescript %} class Songs @_titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. @get_count: -> @_titles constructor: (@artist, @title) -> @constructor._titles++ # Refers to ._titles, in this case Songs.titles Songs.get_count() # => 0 song = new Songs("Rick Astley", "Never Gonna Give You Up") Songs.get_count() # => 1 song.get_count() # => TypeError: Object has no method 'get_count' {% endhighlight %} ### Instance Method {% highlight coffeescript %} class Songs _titles: 0 # Although it's directly accessible, the leading _ defines it by convention as private property. get_count: -> @_titles constructor: (@artist, @title) -> @_titles++ song = new Songs("Rick Astley", "Never Gonna Give You Up") song.get_count() # => 1 Songs.get_count() # => TypeError: Object function Songs(artist, title) ... has no method 'get_count' {% endhighlight %} ## Discussion Coffeescript will store class methods (also called static methods) on the object itself rather than on the object prototype (and thus on individual object instances), which conserves memory and gives a central location to store class-level values. ================================================ FILE: chapters/classes_and_objects/class-variables-and-instance-variables.md ================================================ --- layout: recipe title: Class Variables and Instance Variables chapter: Classes and Objects --- ## Problem You want to create class variables and instance variables (properties). ## Solution ### Class Variables {% highlight coffeescript %} class Zoo @MAX_ANIMALS: 50 MAX_ZOOKEEPERS: 3 helpfulInfo: => "Zoos may contain a maximum of #{@constructor.MAX_ANIMALS} animals and #{@MAX_ZOOKEEPERS} zoo keepers." Zoo.MAX_ANIMALS # => 50 Zoo.MAX_ZOOKEEPERS # => undefined (it is a prototype member) Zoo::MAX_ZOOKEEPERS # => 3 zoo = new Zoo zoo.MAX_ZOOKEEPERS # => 3 zoo.helpfulInfo() # => "Zoos may contain a maximum of 50 animals and 3 zoo keepers." zoo.MAX_ZOOKEEPERS = "smelly" zoo.MAX_ANIMALS = "seventeen" zoo.helpfulInfo() # => "Zoos may contain a maximum of 50 animals and smelly zoo keepers." {% endhighlight %} ### Instance Variables You have to define instance variables (i.e. properties) inside a class' method, initialize your defaults in the constructor. {% highlight coffeescript %} class Zoo constructor: -> @animals = [] # Here the instance variable is defined addAnimal: (name) -> @animals.push name zoo = new Zoo() zoo.addAnimal 'elephant' otherZoo = new Zoo() otherZoo.addAnimal 'lion' zoo.animals # => ['elephant'] otherZoo.animals # => ['lion'] {% endhighlight %} #### WARNING! *Do not add the variable accidently to the prototype, by defining it outside the constructor* (Even if mentioned [elsewhere](http://arcturo.github.io/library/coffeescript/03_classes.html#content), this does not work as intended, due to the underlying JavaScript prototype concept). {% highlight coffeescript %} class BadZoo animals: [] # Translates to BadZoo.prototype.animals = []; and is thus shared between instances addAnimal: (name) -> @animals.push name # Works due to the prototype concept of Javascript zoo = new BadZoo() zoo.addAnimal 'elephant' otherZoo = new BadZoo() otherZoo.addAnimal 'lion' zoo.animals # => ['elephant','lion'] # Oops... otherZoo.animals # => ['elephant','lion'] # Oops... BadZoo::animals # => ['elephant','lion'] # The value is stored in the prototype {% endhighlight %} ## Discussion Coffeescript will store the values of class variables on the class itself rather than on the prototype it defines. These are useful for defining variables on classes which can't be overwritten by instance attribute variables. ================================================ FILE: chapters/classes_and_objects/cloning.md ================================================ --- layout: recipe title: Cloning an Object (Deep Copy) chapter: Classes and Objects --- ## Problem You want to clone an object with all its sub-objects. ## Solution {% highlight coffeescript %} clone = (obj) -> if not obj? or typeof obj isnt 'object' return obj if obj instanceof Date return new Date(obj.getTime()) if obj instanceof RegExp flags = '' flags += 'g' if obj.global? flags += 'i' if obj.ignoreCase? flags += 'm' if obj.multiline? flags += 'y' if obj.sticky? return new RegExp(obj.source, flags) newInstance = new obj.constructor() for key of obj newInstance[key] = clone obj[key] return newInstance x = foo: 'bar' bar: 'foo' y = clone(x) y.foo = 'test' console.log x.foo isnt y.foo, x.foo, y.foo # => true, bar, test {% endhighlight %} ## Discussion The difference between copying an object through assignment and through this clone-function is how they handle references. The assignment only copies the object's reference, whereas the clone-function creates a complete new object by * creating a new object like the source object, * copying all attributes form the source object to the new object and * repeating these steps for all sub-objects by calling the clone-function recursively. Example of an assignment copy: {% highlight coffeescript %} x = foo: 'bar' bar: 'foo' y = x y.foo = 'test' console.log x.foo isnt y.foo, x.foo, y.foo # => false, test, test {% endhighlight %} As you can see, when you change `y` after the copy, you also change `x`. ================================================ FILE: chapters/classes_and_objects/index.html ================================================ --- layout: chapter title: Classes and Objects chapter: Classes and Objects --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/classes_and_objects/mixins.md ================================================ --- layout: recipe title: Mixins for classes chapter: Classes and Objects --- ## Problem You have a few utility methods that you want to include in a number of different classes. ## Solution Use a mixOf factory function that generates a mixed superclass for you. {% highlight coffeescript %} mixOf = (base, mixins...) -> class Mixed extends base for mixin in mixins by -1 #earlier mixins override later ones for name, method of mixin:: Mixed::[name] = method Mixed ... class DeepThought answer: -> 42 class PhilosopherMixin pontificate: -> console.log "hmm..." @wise = yes class DeeperThought extends mixOf DeepThought, PhilosopherMixin answer: -> @pontificate() super() earth = new DeeperThought earth.answer() # hmm... # => 42 {% endhighlight %} ## Discussion This is intended for lightweight mixins. Thus you inherit methods of the base and its ancestors, and those of the mixins, but not those of the ancestors of the mixins. Also, after declaring a mixed class, further changes in the mixins are not reflected. ================================================ FILE: chapters/classes_and_objects/object-literal.md ================================================ --- layout: recipe title: Create an Object Literal if It Does Not Already Exist chapter: Classes and Objects --- ## Problem You want to initialize an object literal, but you do not want to overwrite the object if it already exists. ## Solution Use the Existential operator {% highlight coffeescript %} window.MY_NAMESPACE ?= {} {% endhighlight %} ## Discussion This is equivalent to the following JavaScript: {% highlight javascript %} if(window.MY_NAMESPACE === null || window.MY_NAMESPACE === undefined) { window.MY_NAMESPACE = {}; } {% endhighlight %} ## Problem You want to make a conditonal assignment if it does not exists or if it is falsy (empty, 0, null, false) ## Solution Use the Conditional assignment operator {% highlight coffeescript %} window.my_variable ||= {} {% endhighlight %} ## Discussion This is equivalent to the following JavaScript: {% highlight javascript %} window.my_variable = window.my_variable || {}; {% endhighlight %} Common JavaScript technique, using conditional assignment to ensure that we have an object that is not falsy ================================================ FILE: chapters/classes_and_objects/type-function.md ================================================ --- layout: recipe title: A CoffeeScript Type Function chapter: Classes and Objects --- ## Problem You'd like to know the type of a object without using typeof. (See http://javascript.crockford.com/remedial.html for more information on why typeof is pretty inferior.) ## Solution Use the following function: {% highlight coffeescript %} type = (obj) -> if obj == undefined or obj == null return String obj classToType = { '[object Boolean]': 'boolean', '[object Number]': 'number', '[object String]': 'string', '[object Function]': 'function', '[object Array]': 'array', '[object Date]': 'date', '[object RegExp]': 'regexp', '[object Object]': 'object' } return classToType[Object.prototype.toString.call(obj)] {% endhighlight %} ## Discussion This function was modeled on jQuery's [$.type function](http://api.jquery.com/jQuery.type/). Note that, as an alternative to type checking, you can often use duck typing and the existential operator together to eliminating the need to examine an object's type, in certain cases. For example, here is exception-free code that pushes an element to an array, if myArray is in fact an array (or array-like, with a push function), and does nothing otherwise. {% highlight coffeescript %} myArray?.push? myValue {% endhighlight %} ================================================ FILE: chapters/databases/index.html ================================================ --- layout: chapter title: Databases chapter: Databases --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/databases/mongodb.md ================================================ --- layout: recipe title: MongoDB chapter: Databases --- ## Problem You need to interface with a MongoDB database. ## Solution ### For Node.js ### Setup * [Install MongoDB](http://www.mongodb.org/display/DOCS/Quickstart) on your computer if you have not already. * [Install the native MongoDB module](https://github.com/christkv/node-mongodb-native). #### Saving Records {% highlight coffeescript %} mongo = require 'mongodb' server = new mongo.Server "127.0.0.1", 27017, {} client = new mongo.Db 'test', server, {w:1} # save() updates existing records or inserts new ones as needed exampleSave = (dbErr, collection) -> console.log "Unable to access database: #{dbErr}" if dbErr collection.save { _id: "my_favorite_latte", flavor: "honeysuckle" }, (err, docs) -> console.log "Unable to save record: #{err}" if err client.close() client.open (err, database) -> client.collection 'coffeescript_example', exampleSave {% endhighlight %} #### Finding Records {% highlight coffeescript %} mongo = require 'mongodb' server = new mongo.Server "127.0.0.1", 27017, {} client = new mongo.Db 'test', server, {w:1} exampleFind = (dbErr, collection) -> console.log "Unable to access database: #{dbErr}" if dbErr collection.find({ _id: "my_favorite_latte" }).nextObject (err, result) -> if err console.log "Unable to find record: #{err}" else console.log result # => { id: "my_favorite_latte", flavor: "honeysuckle" } client.close() client.open (err, database) -> client.collection 'coffeescript_example', exampleFind {% endhighlight %} ### For Browsers A [REST-based interface](https://github.com/tdegrunt/mongodb-rest) is in the works. This will provide AJAX-based access. ## Discussion This recipe breaks the *save* and *find* into separate examples in order to separate the MongoDB-specific concerns from the task of connection and callback management. The [async module](https://github.com/caolan/async) can help with that. ================================================ FILE: chapters/databases/sqlite.md ================================================ --- layout: recipe title: SQLite chapter: Databases --- ## Problem You need to interface with a [SQLite](http://www.sqlite.org/) database from inside of Node.js. ## Solution Use the [SQLite module](http://code.google.com/p/node-sqlite/). {% highlight coffeescript %} sqlite = require 'sqlite' db = new sqlite.Database # The module uses asynchronous methods, # so we chain the calls the db.execute exampleCreate = -> db.execute "CREATE TABLE snacks (name TEXT(25), flavor TEXT(25))", (exeErr, rows) -> throw exeErr if exeErr exampleInsert() exampleInsert = -> db.execute "INSERT INTO snacks (name, flavor) VALUES ($name, $flavor)", { $name: "Potato Chips", $flavor: "BBQ" }, (exeErr, rows) -> throw exeErr if exeErr exampleSelect() exampleSelect = -> db.execute "SELECT name, flavor FROM snacks", (exeErr, rows) -> throw exeErr if exeErr console.log rows[0] # => { name: 'Potato Chips', flavor: 'BBQ' } # :memory: creates a DB in RAM # You can supply a filepath (like './example.sqlite') to create/open one on disk db.open ":memory:", (openErr) -> throw openErr if openErr exampleCreate() {% endhighlight %} ## Discussion You can also prepare your SQL queries beforehand: {% highlight coffeescript %} sqlite = require 'sqlite' async = require 'async' # Not required but added to make the example more concise db = new sqlite.Database createSQL = "CREATE TABLE drinks (name TEXT(25), price NUM)" insertSQL = "INSERT INTO drinks (name, price) VALUES (?, ?)" selectSQL = "SELECT name, price FROM drinks WHERE price < ?" create = (onFinish) -> db.execute createSQL, (exeErr) -> throw exeErr if exeErr onFinish() prepareInsert = (name, price, onFinish) -> db.prepare insertSQL, (prepErr, statement) -> statement.bindArray [name, price], (bindErr) -> statement.fetchAll (fetchErr, rows) -> # Called so that it executes the insert onFinish() prepareSelect = (onFinish) -> db.prepare selectSQL, (prepErr, statement) -> statement.bindArray [1.00], (bindErr) -> statement.fetchAll (fetchErr, rows) -> console.log rows[0] # => { name: "Mia's Root Beer", price: 0.75 } onFinish() db.open ":memory:", (openErr) -> async.series([ (onFinish) -> create onFinish, (onFinish) -> prepareInsert "LunaSqueeze", 7.95, onFinish, (onFinish) -> prepareInsert "Viking Sparkling Grog", 4.00, onFinish, (onFinish) -> prepareInsert "Mia's Root Beer", 0.75, onFinish, (onFinish) -> prepareSelect onFinish ]) {% endhighlight %} The [SQLite version of SQL](http://www.sqlite.org/lang.html) and the [node-sqlite module documentation](https://github.com/orlandov/node-sqlite#readme) provide more complete information. ================================================ FILE: chapters/dates_and_times/date-of-easter.md ================================================ --- layout: recipe title: Calculate the Date of Easter Sunday chapter: Dates and Times --- ## Problem You need to find the month and day of the Easter Sunday for given year. ## Solution The following function returns array with two elements: month (1-12) and day of the Easter Sunday. If no arguments are given result is for the current year. This is an implementation of [Anonymous Gregorian algorithm](http://en.wikipedia.org/wiki/Computus#Anonymous_Gregorian_algorithm) in CoffeeScript. {% highlight coffeescript %} gregorianEaster = (year = (new Date).getFullYear()) -> a = year % 19 b = ~~(year / 100) c = year % 100 d = ~~(b / 4) e = b % 4 f = ~~((b + 8) / 25) g = ~~((b - f + 1) / 3) h = (19 * a + b - d - g + 15) % 30 i = ~~(c / 4) k = c % 4 l = (32 + 2 * e + 2 * i - h - k) % 7 m = ~~((a + 11 * h + 22 * l) / 451) n = h + l - 7 * m + 114 month = ~~(n / 31) day = (n % 31) + 1 [month, day] {% endhighlight %} ## Discussion NB! Javascript numbers months from 0 to 11 so .getMonth() for date in March will return 2, this function will return 3. You can modify the function if you want this to be consistent. The function uses ~~ trick instead of Math.floor(). {% highlight coffeescript %} gregorianEaster() # => [4, 24] (April 24th in 2011) gregorianEaster 1972 # => [4, 2] {% endhighlight %} ================================================ FILE: chapters/dates_and_times/date-of-thanksgiving.md ================================================ --- layout: recipe title: Calculate the Date of Thanksgiving (USA and Canada) chapter: Dates and Times --- ## Problem You need to calculate when Thanksgiving is in a given year. ## Solution The following functions return the day of Thanksgiving for a given year. If no year is given then current year is used. In the USA Thanksgiving is celebrated on the fourth Thursday in November: {% highlight coffeescript %} thanksgivingDayUSA = (year = (new Date).getFullYear()) -> first = new Date year, 10, 1 day_of_week = first.getDay() 22 + (11 - day_of_week) % 7 {% endhighlight %} In Canada it is the second Monday in October: {% highlight coffeescript %} thanksgivingDayCA = (year = (new Date).getFullYear()) -> first = new Date year, 9, 1 day_of_week = first.getDay() 8 + (8 - day_of_week) % 7 {% endhighlight %} ## Discussion {% highlight coffeescript %} thanksgivingDayUSA() #=> 24 (November 24th, 2011) thanksgivingDayCA() # => 10 (October 10th, 2011) thanksgivingDayUSA(2012) # => 22 (November 22nd) thanksgivingDayCA(2012) # => 8 (October 8th) {% endhighlight %} The idea is very simple: 1. Find out what day of the week is the first day of respective month (November for USA, October for Canada). 2. Calculate offset from that day to the next occurrence of weekday required (Thursday for USA, Monday for Canada). 3. Add that offset to the first possible date of the holiday (22nd for USA Thanksgiving, 8th for Canada). ================================================ FILE: chapters/dates_and_times/days-between-two-dates.md ================================================ --- layout: recipe title: Get Days Between Two Dates chapter: Dates and Times --- ## Problem You need to find how many seconds, minutes, hours, days, months or years have passed between two dates. ## Solution Use JavaScript's Date function getTime(). Which provides how much time in milliseconds have passed since 01/01/1970: {% highlight coffeescript %} DAY = 1000 * 60 * 60 * 24 d1 = new Date('02/01/2011') d2 = new Date('02/06/2011') days_passed = Math.round((d2.getTime() - d1.getTime()) / DAY) {% endhighlight %} ## Discussion Using milliseconds makes the life easier to avoid overflow mistakes with Dates. So we first calculate how many milliseconds are in a day. Then, given two distinct dates, get the difference in milliseconds between two dates and then divide by how many milliseconds are in a day. It will return the days between two distinct dates. If you'd like to calculate the hours between two date objects, you can do that by dividing the difference in milliseconds by the conversion of milliseconds to hours. The same goes for minutes and seconds. {% highlight coffeescript %} HOUR = 1000 * 60 * 60 d1 = new Date('02/01/2011 02:20') d2 = new Date('02/06/2011 05:20') hour_passed = Math.round((d2.getTime() - d1.getTime()) / HOUR) {% endhighlight %} ================================================ FILE: chapters/dates_and_times/finding-last-day-of-the-month.md ================================================ --- layout: recipe title: Finding the Last Day of the Month chapter: Dates and Times --- ## Problem You need to find the last day of the month, but don't want to keep a lookup table of the number of days in each month of the year. ## Solution Use JavaScript's Date underflow to find the -1th day of the following month: {% highlight coffeescript %} now = new Date lastDayOfTheMonth = new Date(1900+now.getYear(), now.getMonth()+1, 0) {% endhighlight %} ## Discussion JavaScript's Date constructor cheerfully handles overflow and underflow conditions, which makes date math very easy. Given this ease of manipulation, it doesn't make sense to worry about how many days are in a given month; just nudge the math around. In December, the solution above will actually ask for the 0th day of the 13th month of the current year, which works out to the day before the 1st day of January of NEXT year, which works out to the 31st day of December of the current year. ================================================ FILE: chapters/dates_and_times/finding-last-or-next-month.md ================================================ --- layout: recipe title: Finding Last (or Next) Month chapter: Dates and Times --- ## Problem You need to calculate a relative date range like "last month" or "next month". ## Solution Add or subtract from the current month, secure in the knowledge that JavaScript's Date constructor will fix up the math. {% highlight coffeescript %} # these examples were written in GMT-6 # Note that these examples WILL work in January! now = new Date # => "Sun, 08 May 2011 05:50:52 GMT" lastMonthStart = new Date 1900+now.getYear(), now.getMonth()-1, 1 # => "Fri, 01 Apr 2011 06:00:00 GMT" lastMonthEnd = new Date 1900+now.getYear(), now.getMonth(), 0 # => "Sat, 30 Apr 2011 06:00:00 GMT" {% endhighlight %} ## Discussion JavaScript Date objects will cheerfully handle underflows and overflows in the month and day fields, and will adjust the date object accordingly. You can ask for the 42nd of March, for example, and will get the 11th of April. JavaScript Date objects store the year as the number of years since 1900, the month as an integer from 0 to 11, and the date (day of month) as an integer from 1 to 31. In the solution above, last_month_start is obtained by asking for the first day of a month in the current year, but the month is -1 to 10. If month is -1 the Date object will actually return December of the previous year: {% highlight coffeescript %} lastNewYearsEve = new Date 1900+now.getYear(), -1, 31 # => "Fri, 31 Dec 2010 07:00:00 GMT" {% endhighlight %} The same is true for overflows: {% highlight coffeescript %} thirtyNinthOfFourteember = new Date 1900+now.getYear(), 13, 39 # => "Sat, 10 Mar 2012 07:00:00 GMT" {% endhighlight %} ================================================ FILE: chapters/dates_and_times/index.html ================================================ --- layout: chapter title: Dates and Times chapter: Dates and Times --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/dates_and_times/moon-phase-for-date.md ================================================ --- layout: recipe title: Calculate Phase of the Moon for a Date chapter: Dates and Times --- ## Problem You want to find the current phase of the moon. ## Solution The following code provides a method to calculate the phase of the moon for a given date. {% highlight coffeescript %} # moonPhase.coffee # Moon-phase calculator # Roger W. Sinnott, Sky & Telescope, June 16, 2006 # http://www.skyandtelescope.com/observing/objects/javascript/moon_phases # # Translated to CoffeeScript by Mike Hatfield @WebCoding4Fun proper_ang = (big) -> tmp = 0 if big > 0 tmp = big / 360.0 tmp = (tmp - (~~tmp)) * 360.0 else tmp = Math.ceil(Math.abs(big / 360.0)) tmp = big + tmp * 360.0 tmp jdn = (date) -> month = date.getMonth() day = date.getDate() year = date.getFullYear() zone = date.getTimezoneOffset() / 1440 mm = month dd = day yy = year yyy = yy mmm = mm if mm < 3 yyy = yyy - 1 mmm = mm + 12 day = dd + zone + 0.5 a = ~~( yyy / 100 ) b = 2 - a + ~~( a / 4 ) jd = ~~( 365.25 * yyy ) + ~~( 30.6001 * ( mmm+ 1 ) ) + day + 1720994.5 jd + b if jd > 2299160.4999999 moonElong = (jd) -> dr = Math.PI / 180 rd = 1 / dr meeDT = Math.pow((jd - 2382148), 2) / (41048480 * 86400) meeT = (jd + meeDT - 2451545.0) / 36525 meeT2 = Math.pow(meeT, 2) meeT3 = Math.pow(meeT, 3) meeD = 297.85 + (445267.1115 * meeT) - (0.0016300 * meeT2) + (meeT3 / 545868) meeD = (proper_ang meeD) * dr meeM1 = 134.96 + (477198.8676 * meeT) + (0.0089970 * meeT2) + (meeT3 / 69699) meeM1 = (proper_ang meeM1) * dr meeM = 357.53 + (35999.0503 * meeT) meeM = (proper_ang meeM) * dr elong = meeD * rd + 6.29 * Math.sin( meeM1 ) elong = elong - 2.10 * Math.sin( meeM ) elong = elong + 1.27 * Math.sin( 2*meeD - meeM1 ) elong = elong + 0.66 * Math.sin( 2*meeD ) elong = proper_ang elong elong = Math.round elong moonNum = ( ( elong + 6.43 ) / 360 ) * 28 moonNum = ~~( moonNum ) if moonNum is 28 then 0 else moonNum getMoonPhase = (age) -> moonPhase = "new Moon" moonPhase = "first quarter" if age > 3 and age < 11 moonPhase = "full Moon" if age > 10 and age < 18 moonPhase = "last quarter" if age > 17 and age < 25 if ((age is 1) or (age is 8) or (age is 15) or (age is 22)) moonPhase = "1 day past " + moonPhase if ((age is 2) or (age is 9) or (age is 16) or (age is 23)) moonPhase = "2 days past " + moonPhase if ((age is 3) or (age is 1) or (age is 17) or (age is 24)) moonPhase = "3 days past " + moonPhase if ((age is 4) or (age is 11) or (age is 18) or (age is 25)) moonPhase = "3 days before " + moonPhase if ((age is 5) or (age is 12) or (age is 19) or (age is 26)) moonPhase = "2 days before " + moonPhase if ((age is 6) or (age is 13) or (age is 20) or (age is 27)) moonPhase = "1 day before " + moonPhase moonPhase MoonPhase = exports? and exports or @MoonPhase = {} class MoonPhase.Calculator getMoonDays: (date) -> jd = jdn date moonElong jd getMoonPhase: (date) -> jd = jdn date getMoonPhase( moonElong jd ) {% endhighlight %} ## Discussion This code exposes a MoonPhase Calculator object with two methods. Calculator -> getMoonPhase will return a text representation of the lunar phase for the date provided. This can be used in both the browser and Node.js. {% highlight console %} $ node > var MoonPhase = require('./moonPhase.js'); undefined > var calc = new MoonPhase.Calculator(); undefined > calc.getMoonPhase(new Date()); 'full moon' > calc.getMoonPhase(new Date(1972, 6, 30)); '3 days before last quarter' {% endhighlight %} ================================================ FILE: chapters/design_patterns/adapter.md ================================================ --- layout: recipe title: Adapter pattern chapter: Design patterns --- ## Problem Imagine you are traveling to a foreign country and once at your hotel room you realize your power cord socket is not compatible with the wall socket. Luckily, you remembered you've brought your power adapter with you. It will connect your power cord socket on one side and wall socket on the other side, allowing for communication between them. The same situation may arise in code, when 2 (or more) instances (of classes, modules, etc.) want to talk to each other, but whose communication protocol (e.i. the language they use to communicate) is different from each other. In such a situation, the [Adapter Pattern]({{ site.baseurl }}//en.wikipedia.org/wiki/Adapter_pattern) comes in handy. It will do the translation, from one side to the other. ## Solution {% highlight coffeescript %} # a fragment of 3-rd party grid component class AwesomeGrid constructor: (@datasource)-> @sort_order = 'ASC' @sorter = new NullSorter # in this place we use NullObject pattern (another useful pattern) setCustomSorter: (@customSorter) -> @sorter = customSorter sort: () -> @datasource = @sorter.sort @datasource, @sort_order # don't forget to change sort order class NullSorter sort: (data, order) -> # do nothing; it is just a stub class RandomSorter sort: (data)-> for i in [data.length-1..1] #let's shuffle the data a bit j = Math.floor Math.random() * (i + 1) [data[i], data[j]] = [data[j], data[i]] return data class RandomSorterAdapter constructor: (@sorter) -> sort: (data, order) -> @sorter.sort data agrid = new AwesomeGrid ['a','b','c','d','e','f'] agrid.setCustomSorter new RandomSorterAdapter(new RandomSorter) agrid.sort() # sort data with custom sorter through adapter {% endhighlight %} ## Discussion Adapter is useful when you have to organize an interaction between two objects with different interfaces. It can happen when you use 3rd party libraries or you work with legacy code. In any case be careful with adapter: it can be helpful but it can instigate design errors. ================================================ FILE: chapters/design_patterns/bridge.md ================================================ --- layout: recipe title: Bridge Pattern chapter: Design Patterns --- ## Problem You need to maintain a reliable interface for code that can change frequently or change between multiple implementations. ## Solution Use the Bridge pattern as an intermediate between the different implementations and the rest of the code. Assume that you developed an in-browser text editor that saves to the cloud. Now, however, you need to port it to a stand-alone client that saves locally. {% highlight coffeescript %} class TextSaver constructor: (@filename, @options) -> save: (data) -> class CloudSaver extends TextSaver constructor: (@filename, @options) -> super @filename, @options save: (data) -> # Assuming jQuery # Note the fat arrows $( => $.post "#{@options.url}/#{@filename}", data, => alert "Saved '#{data}' to #{@filename} at #{@options.url}." ) class FileSaver extends TextSaver constructor: (@filename, @options) -> super @filename, @options @fs = require 'fs' save: (data) -> @fs.writeFile @filename, data, (err) => # Note the fat arrow if err? then console.log err else console.log "Saved '#{data}' to #{@filename} in #{@options.directory}." filename = "temp.txt" data = "Example data" saver = if window? new CloudSaver filename, url: 'http://localhost' # => Saved "Example data" to temp.txt at http://localhost else if root? new FileSaver filename, directory: './' # => Saved "Example data" to temp.txt in ./ saver.save data {% endhighlight %} ## Discussion The Bridge pattern helps you to move the implementation-specific code out of sight so that you can focus on your program's specific code. In the above example, the rest of your application can call `saver.save data` without regard for where the file ultimately ends up. ================================================ FILE: chapters/design_patterns/builder.md ================================================ --- layout: recipe title: Builder Pattern chapter: Design Patterns --- ## Problem You need to prepare a complicated, multi-part object, but you expect to do it more than once or with varying configurations. ## Solution Create a Builder to encapsulate the object production process. The [Todo.txt](http://todotxt.com) format provides an advanced but still plain-text method for maintaining lists of to-do items. Typing out each item by hand would provide exhausting and error-prone, however, so a TodoTxtBuilder class could save us the trouble: {% highlight coffeescript %} class TodoTxtBuilder constructor: (defaultParameters={ }) -> @date = new Date(defaultParameters.date) or new Date @contexts = defaultParameters.contexts or [ ] @projects = defaultParameters.projects or [ ] @priority = defaultParameters.priority or undefined newTodo: (description, parameters={ }) -> date = (parameters.date and new Date(parameters.date)) or @date contexts = @contexts.concat(parameters.contexts or [ ]) projects = @projects.concat(parameters.projects or [ ]) priorityLevel = parameters.priority or @priority createdAt = [date.getFullYear(), date.getMonth()+1, date.getDate()].join("-") contextNames = ("@#{context}" for context in contexts when context).join(" ") projectNames = ("+#{project}" for project in projects when project).join(" ") priority = if priorityLevel then "(#{priorityLevel})" else "" todoParts = [priority, createdAt, description, contextNames, projectNames] (part for part in todoParts when part.length > 0).join " " builder = new TodoTxtBuilder(date: "10/13/2011") builder.newTodo "Wash laundry" # => '2011-10-13 Wash laundry' workBuilder = new TodoTxtBuilder(date: "10/13/2011", contexts: ["work"]) workBuilder.newTodo "Show the new design pattern to Lucy", contexts: ["desk", "xpSession"] # => '2011-10-13 Show the new design pattern to Lucy @work @desk @xpSession' workBuilder.newTodo "Remind Sean about the failing unit tests", contexts: ["meeting"], projects: ["compilerRefactor"], priority: 'A' # => '(A) 2011-10-13 Remind Sean about the failing unit tests @work @meeting +compilerRefactor' {% endhighlight %} ## Discussion The TodoTxtBuilder class takes care of all the heavy lifting of text generation and lets the programmer focus on the unique elements of each to-do item. Additionally, a command line tool or GUI could plug into this code and still retain support for later, more advanced versions of the format with ease. ### Pre-Construction Instead of creating a new instance of the needed object from scratch every time, we shift the burden to a separate object that we can then tweak during the object creation process. {% highlight coffeescript %} builder = new TodoTxtBuilder(date: "10/13/2011") builder.newTodo "Order new netbook" # => '2011-10-13 Order new netbook' builder.projects.push "summerVacation" builder.newTodo "Buy suntan lotion" # => '2011-10-13 Buy suntan lotion +summerVacation' builder.contexts.push "phone" builder.newTodo "Order tickets" # => '2011-10-13 Order tickets @phone +summerVacation' delete builder.contexts[0] builder.newTodo "Fill gas tank" # => '2011-10-13 Fill gas tank +summerVacation' {% endhighlight %} ### Exercises * Expand the project- and context-tag generation code to filter out duplicate entries. * Some Todo.txt users like to insert project and context tags inside the description of their to-do items. Add code to identify these tags and filter them out of the end tags. ================================================ FILE: chapters/design_patterns/command.md ================================================ --- layout: recipe title: Command Pattern chapter: Design Patterns --- ## Problem You need to let another object handle when your private code is executed. ## Solution Use the [Command pattern](http://en.wikipedia.org/wiki/Command_pattern) to pass along references to your functions. {% highlight coffeescript %} # Using a private variable to simulate external scripts or modules incrementers = (() -> privateVar = 0 singleIncrementer = () -> privateVar += 1 doubleIncrementer = () -> privateVar += 2 commands = single: singleIncrementer double: doubleIncrementer value: -> privateVar )() class RunsAll constructor: (@commands...) -> run: -> command() for command in @commands runner = new RunsAll(incrementers.single, incrementers.double, incrementers.single, incrementers.double) runner.run() incrementers.value() # => 6 {% endhighlight %} ## Discussion With functions as first-class objects and with the function-bound variable scope inherited from Javascript, the CoffeeScript language makes the pattern nearly invisible. In fact, any function passed along as callbacks can act as a *Command*. The `jqXHR` object returned by jQuery AJAX methods uses this pattern. {% highlight coffeescript %} jqxhr = $.ajax url: "/" logMessages = "" jqxhr.success -> logMessages += "Success!\n" jqxhr.error -> logMessages += "Error!\n" jqxhr.complete -> logMessages += "Completed!\n" # On a valid AJAX request: # logMessages == "Success!\nCompleted!\n" {% endhighlight %} ================================================ FILE: chapters/design_patterns/decorator.md ================================================ --- layout: recipe title: Decorator Pattern chapter: Design Patterns --- ## Problem You have a set of data that you need to process in multiple, possibly varying ways. ## Solution Use the Decorator pattern in order to structure how you apply the changes. {% highlight coffeescript %} miniMarkdown = (line) -> if match = line.match /^(#+)\s*(.*)$/ headerLevel = match[1].length headerText = match[2] "#{headerText}" else if line.length > 0 "

#{line}

" else '' stripComments = (line) -> line.replace /\s*\/\/.*$/, '' # Removes one-line, double-slash C-style comments class TextProcessor constructor: (@processors) -> reducer: (existing, processor) -> if processor processor(existing or '') else existing processLine: (text) -> @processors.reduce @reducer, text processString: (text) -> (@processLine(line) for line in text.split("\n")).join("\n") exampleText = ''' # A level 1 header A regular line // a comment ## A level 2 header A line // with a comment ''' processor = new TextProcessor [stripComments, miniMarkdown] processor.processString exampleText # => "

A level 1 header

\n

A regular line

\n\n

A level 2 header

\n

A line

" {% endhighlight %} ### Results {% highlight html %}

A level 1 header

A regular line

A level 1 header

A line

{% endhighlight %} ## Discussion The TextProcessor serves the role of Decorator by binding the individual, specialized text processors together. This frees up the miniMarkdown and stripComments components to focus on handling nothing but a single line of text. Future developers only have to write functions that return a string and add it to the array of processors. We can even modify the existing Decorator object on the fly: {% highlight coffeescript %} smilies = ':)' : "smile" ':D' : "huge_grin" ':(' : "frown" ';)' : "wink" smilieExpander = (line) -> if line (line = line.replace symbol, "#{text}") for symbol, text of smilies line processor.processors.unshift smilieExpander processor.processString "# A header that makes you :) // you may even laugh" # => "

A header that makes you smile

" processor.processors.shift() # => "

A header that makes you :)

" {% endhighlight %} ================================================ FILE: chapters/design_patterns/factory_method.md ================================================ --- layout: recipe title: Factory Method Pattern chapter: Design Patterns --- ## Problem You don't know what kind of object you will need until runtime. ## Solution Use the [Factory Method](http://en.wikipedia.org/wiki/Factory_method_pattern) pattern and choose the object to be generated dynamically. Say that you need to load a file into an editor but you don't know its format until the user chooses the file. A class using the [Factory Method](http://en.wikipedia.org/wiki/Factory_method_pattern) pattern can serve up different parsers depending on the file's extension. {% highlight coffeescript %} class HTMLParser constructor: -> @type = "HTML parser" class MarkdownParser constructor: -> @type = "Markdown parser" class JSONParser constructor: -> @type = "JSON parser" class ParserFactory makeParser: (filename) -> matches = filename.match /\.(\w*)$/ extension = matches[1] switch extension when "html" then new HTMLParser when "htm" then new HTMLParser when "markdown" then new MarkdownParser when "md" then new MarkdownParser when "json" then new JSONParser factory = new ParserFactory factory.makeParser("example.html").type # => "HTML parser" factory.makeParser("example.md").type # => "Markdown parser" factory.makeParser("example.json").type # => "JSON parser" {% endhighlight %} ## Discussion In the example, you can ignore the specifics of the file's format and focus on the parsed content. A more advanced Factory Method might, for instance, also search for versioning data within the file itself before returning a more precise parser (e.g. an HTML5 parser instead of an HTML v4 parser). ================================================ FILE: chapters/design_patterns/index.html ================================================ --- layout: chapter title: Design Patterns chapter: Design Patterns --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/design_patterns/interpreter.md ================================================ --- layout: recipe title: Interpreter Pattern chapter: Design Patterns --- ## Problem Someone else needs to run parts of your code in a controlled fashion. Alternately, your language of choice cannot express the problem domain in a concise fashion. ## Solution Use the Interpreter pattern to create a domain-specific language that you translate into specific code. Assume, for example, that the user wants to perform math inside of your application. You could let them forward code to _eval_ but that would let them run arbitrary code. Instead, you can provide a miniature "stack calculator" language that you parse separately in order to only run mathematical operations while reporting more useful error messages. {% highlight coffeescript %} class StackCalculator parseString: (string) -> @stack = [ ] for token in string.split /\s+/ @parseToken token if @stack.length > 1 throw "Not enough operators: numbers left over" else @stack[0] parseToken: (token, lastNumber) -> if isNaN parseFloat(token) # Assume that anything other than a number is an operator @parseOperator token else @stack.push parseFloat(token) parseOperator: (operator) -> if @stack.length < 2 throw "Can't operate on a stack without at least 2 items" right = @stack.pop() left = @stack.pop() result = switch operator when "+" then left + right when "-" then left - right when "*" then left * right when "/" if right is 0 throw "Can't divide by 0" else left / right else throw "Unrecognized operator: #{operator}" @stack.push result calc = new StackCalculator calc.parseString "5 5 +" # => { result: 10 } calc.parseString "4.0 5.5 +" # => { result: 9.5 } calc.parseString "5 5 + 5 5 + *" # => { result: 100 } try calc.parseString "5 0 /" catch error error # => "Can't divide by 0" try calc.parseString "5 -" catch error error # => "Can't operate on a stack without at least 2 items" try calc.parseString "5 5 5 -" catch error error # => "Not enough operators: numbers left over" try calc.parseString "5 5 5 foo" catch error error # => "Unrecognized operator: foo" {% endhighlight %} ## Discussion As an alternative to writing our own interpreter, you can co-op the existing CoffeeScript interpreter in a such a way that its normal syntax makes for more natural (and therefore more comprehensible) expressions of your algorithm. {% highlight coffeescript %} class Sandwich constructor: (@customer, @bread='white', @toppings=[], @toasted=false)-> white = (sw) -> sw.bread = 'white' sw wheat = (sw) -> sw.bread = 'wheat' sw turkey = (sw) -> sw.toppings.push 'turkey' sw ham = (sw) -> sw.toppings.push 'ham' sw swiss = (sw) -> sw.toppings.push 'swiss' sw mayo = (sw) -> sw.toppings.push 'mayo' sw toasted = (sw) -> sw.toasted = true sw sandwich = (customer) -> new Sandwich customer to = (customer) -> customer send = (sw) -> toastedState = sw.toasted and 'a toasted' or 'an untoasted' toppingState = '' if sw.toppings.length > 0 if sw.toppings.length > 1 toppingState = " with #{sw.toppings[0..sw.toppings.length-2].join ', '} and #{sw.toppings[sw.toppings.length-1]}" else toppingState = " with #{sw.toppings[0]}" "#{sw.customer} requested #{toastedState}, #{sw.bread} bread sandwich#{toppingState}" send sandwich to 'Charlie' # => "Charlie requested an untoasted, white bread sandwich" send turkey sandwich to 'Judy' # => "Judy requested an untoasted, white bread sandwich with turkey" send toasted ham turkey sandwich to 'Rachel' # => "Rachel requested a toasted, white bread sandwich with turkey and ham" send toasted turkey ham swiss sandwich to 'Matt' # => "Matt requested a toasted, white bread sandwich with swiss, ham and turkey" {% endhighlight %} This example allows for layers of functions by how it returns the modified object so that outer functions can modify it in turn. By borrowing a verb and the preposition _to_, the example lends natural grammar to the construction and ends up reading like an actual sentence when used correctly. This way, both your CoffeeScript skills and your existing language skills can help catch code problems. ================================================ FILE: chapters/design_patterns/memento.md ================================================ --- layout: recipe title: Memento Pattern chapter: Design Patterns --- ## Problem You want to anticipate the reversion of changes to an object. ## Solution Use the [Memento pattern](http://en.wikipedia.org/wiki/Memento_pattern) to track changes to an object. The class using the pattern will export a `memento` object stored elsewhere. If you have application where the user can edit a text file, for example, they may want to undo their last action. You can save the current state of the file before the user changes it and then roll back to that at a later point. {% highlight coffeescript %} class PreserveableText class Memento constructor: (@text) -> constructor: (@text) -> save: (newText) -> memento = new Memento @text @text = newText memento restore: (memento) -> @text = memento.text pt = new PreserveableText "The original string" pt.text # => "The original string" memento = pt.save "A new string" pt.text # => "A new string" pt.save "Yet another string" pt.text # => "Yet another string" pt.restore memento pt.text # => "The original string" {% endhighlight %} ## Discussion The Memento object returned by `PreserveableText#save` stores the important state information separately for safe-keeping. You could even serialize this Memento in order to maintain an "undo" buffer on the hard disk or remotely for such data-intensive objects as edited images. ================================================ FILE: chapters/design_patterns/observer.md ================================================ --- layout: recipe title: Observer Pattern chapter: Design patterns --- ## Problem You have to notify some objects about an event happen ## Solution Use an [Observer Pattern](http://en.wikipedia.org/wiki/Observer_pattern) {% highlight coffeescript %} class PostOffice constructor: () -> @subscribers = [] notifyNewItemReleased: (item) -> subscriber.callback(item) for subscriber in @subscribers when subscriber.item is item subscribe: (to, onNewItemReleased) -> @subscribers.push {'item':to, 'callback':onNewItemReleased} class MagazineSubscriber onNewMagazine: (item) -> alert "I've got new "+item class NewspaperSubscriber onNewNewspaper: (item) -> alert "I've got new "+item postOffice = new PostOffice() sub1 = new MagazineSubscriber() sub2 = new NewspaperSubscriber() postOffice.subscribe "Mens Health", sub1.onNewMagazine postOffice.subscribe "Times", sub2.onNewNewspaper postOffice.notifyNewItemReleased "Times" postOffice.notifyNewItemReleased "Mens Health" {% endhighlight %} ## Discussion Here you have an observer object (PostOffice) and observable objects (MagazineSubscriber, NewspaperSubscriber). To be notified about an event of publishing new periodical observable object should make subscription on PostOffice. Every of subscribed objects is stored internally in the PostOffice array of subscriptions. Every subscriber is notified on new concrete periodical is published. ================================================ FILE: chapters/design_patterns/singleton.md ================================================ --- layout: recipe title: Singleton Pattern chapter: Design Patterns --- ## Problem Many times you only want one, and only one, instance of a class. For example, you may only need one class that creates server resources and you want to ensure that the one object can control those resources. Beware, however, because the singleton pattern can be easily abused to mimic unwanted global variables. ## Solution The publicly available class only contains the method to get the one true instance. The instance is kept within the closure of that public object and is always returned. This works because CoffeeScript allows you to define executable statements inside a class definition. However, because most CoffeeScript compiles into a [IIFE][] wrapper you do not have to place the private class inside the class definition if this style suits you. The later might be useful when developing modular code such as found in [CommonJS][] (Node.js) or [Require.js][] (See the discussion for an example). [IIFE]: http://benalman.com/news/2010/11/immediately-invoked-function-expression/ [CommonJS]: http://www.commonjs.org/ [Require.js]: http://requirejs.org/ {% highlight coffeescript %} class Singleton # You can add statements inside the class definition # which helps establish private scope (due to closures) # instance is defined as null to force correct scope instance = null # Create a private class that we can initialize however # defined inside this scope to force the use of the # singleton class. class PrivateClass constructor: (@message) -> echo: -> @message # This is a static method used to either retrieve the # instance or create a new one. @get: (message) -> instance ?= new PrivateClass(message) a = Singleton.get "Hello A" a.echo() # => "Hello A" b = Singleton.get "Hello B" b.echo() # => "Hello A" Singleton.instance # => undefined a.instance # => undefined Singleton.PrivateClass # => undefined {% endhighlight %} ## Discussion See in the above example how all instances are outputting from the same instance of the Singleton class. You can also see that the PrivateClass and instance variable are not accessible outside the Singleton class. In essence the Singleton class provides a static method get which returns only one instance of PrivateClass and only one. It also hides the PrivateClass from the world so that you can not create your own. The idea of hiding or making private the inner workings is preference. Especially since by default CoffeeScript wraps the compiled code inside it's own IIFE (closure) allowing you to define classes without worry that it might be accessible from outside the file. In this example, note that I am using the idiomatic module export feature to emphasize the publicly accessible portion of the module. (See this discussion for further explanation on [exporting to the global namespace][1]). [1]: http://stackoverflow.com/questions/4214731/coffeescript-global-variables {% highlight coffeescript %} root = exports ? this # Create a private class that we can initialize however # defined inside the wrapper scope. class ProtectedClass constructor: (@message) -> echo: -> @message class Singleton # You can add statements inside the class definition # which helps establish private scope (due to closures) # instance is defined as null to force correct scope instance = null # This is a static method used to either retrieve the # instance or create a new one. @get: (message) -> instance ?= new ProtectedClass(message) # Export Singleton as a module root.Singleton = Singleton {% endhighlight %} Note how incredibly simple coffeescript makes this design pattern. For reference and discussion on nice javascript implementations, check out [Essential JavaScript Design Patterns For Beginners](http://addyosmani.com/resources/essentialjsdesignpatterns/book/). ================================================ FILE: chapters/design_patterns/strategy.md ================================================ --- layout: recipe title: Strategy Pattern chapter: Design Patterns --- ## Problem You have more than one way to solve a problem but you need to choose (or even switch) between these methods at run time. ## Solution Encapsulate your algorithms inside of Strategy objects. Given an unsorted list, for example, we can change the sorting algorithm under different circumstances. ###The base class: {% highlight coffeescript %} StringSorter = (algorithm) -> sort: (list) -> algorithm list {% endhighlight %} ###The strategies: {% highlight coffeescript %} bubbleSort = (list) -> anySwaps = false swapPass = -> for r in [0..list.length-2] if list[r] > list[r+1] anySwaps = true [list[r], list[r+1]] = [list[r+1], list[r]] swapPass() while anySwaps anySwaps = false swapPass() list reverseBubbleSort = (list) -> anySwaps = false swapPass = -> for r in [list.length-1..1] if list[r] < list[r-1] anySwaps = true [list[r], list[r-1]] = [list[r-1], list[r]] swapPass() while anySwaps anySwaps = false swapPass() list {% endhighlight %} ###Using the strategies: {% highlight coffeescript %} sorter = new StringSorter bubbleSort unsortedList = ['e', 'b', 'd', 'c', 'x', 'a'] sorter.sort unsortedList # => ['a', 'b', 'c', 'd', 'e', 'x'] unsortedList.push 'w' # => ['a', 'b', 'c', 'd', 'e', 'x', 'w'] sorter.algorithm = reverseBubbleSort sorter.sort unsortedList # => ['a', 'b', 'c', 'd', 'e', 'w', 'x'] {% endhighlight %} ## Discussion "No plan survives first contact with the enemy", nor users, but we can use the knowledge gained from changing circumstances to adapt. Near the end of the example, for instance, the newest item in the array now lies out of order. Knowing that detail, we can then speed the sort up by switching to an algorithm optimized for that exact scenario with nothing but a simple reassignment. ### Exercises * Expand `StringSorter` into an `AlwaysSortedArray` class that implements all of the functionality of a regular array but which automatically sorts new items based on the method of insertion (e.g. `push` vs. `shift`). ================================================ FILE: chapters/design_patterns/template_method.md ================================================ --- layout: recipe title: Template Method Pattern chapter: Design Patterns --- ## Problem Define the structure of an algorithm as a series of high-level steps, making it possible to specify the behaviour of each step, giving rise to a family of algorithms that have the same structure but different behaviours. ## Solution Use the Template Method to describe the algorithm structure in a superclass, delegating the implementation of some steps to one or more concrete subclasses. For example, imagine you wish to model the production of various types of document and each one may contain a header and a body. {% highlight coffeescript %} class Document produceDocument: -> @produceHeader() @produceBody() produceHeader: -> produceBody: -> class DocWithHeader extends Document produceHeader: -> console.log "Producing header for DocWithHeader" produceBody: -> console.log "Producing body for DocWithHeader" class DocWithoutHeader extends Document produceBody: -> console.log "Producing body for DocWithoutHeader" docs = [new DocWithHeader, new DocWithoutHeader] doc.produceDocument() for doc in docs {% endhighlight %} ## Discussion In this example, the algorithm consists of two steps describing document production: one for producing a document header and the second for producing the document body. An empty method implementation for each step is present in the superclass and polymorphism is exploited such that each concrete subclass can provide a different implementation for a step by overriding a step method. In this example,the DocWithHeader implements both the body and header steps, whereas the DocWithoutHeader only implements the body step. The production of different types of document is then straightforward when document objects are stored in an array, and it is then a simple of matter of iterating over each document object and calling its produceDocument method. ================================================ FILE: chapters/functions/debounce.md ================================================ --- layout: recipe title: Debounce Functions chapter: Functions --- ## Problem You want to execute a function only once, coalescing multiple sequential calls into a single execution at the beginning or end. ## Solution With a named function: {% highlight coffeescript %} debounce: (func, threshold, execAsap) -> timeout = null (args...) -> obj = this delayed = -> func.apply(obj, args) unless execAsap timeout = null if timeout clearTimeout(timeout) else if (execAsap) func.apply(obj, args) timeout = setTimeout delayed, threshold || 100 {% endhighlight %} {% highlight coffeescript %} mouseMoveHandler: (e) -> @debounce((e) -> # Do something here, but only once 300 milliseconds after the mouse cursor stops. 300) someOtherHandler: (e) -> @debounce((e) -> # Do something here, but only once 250 milliseconds after initial execution. 250, true) {% endhighlight %} ## Discussion Learn about [debouncing JavaScript methods](http://unscriptable.com/2009/03/20/debouncing-javascript-methods/) at John Hann's excellent blog article. ================================================ FILE: chapters/functions/index.html ================================================ --- layout: chapter title: Functions chapter: Functions --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/functions/parentheses.md ================================================ --- layout: recipe title: When Function Parentheses Are Not Optional chapter: Functions --- ## Problem You want to call a function that takes no arguments, but don't want to use parentheses. ## Solution Use parentheses anyway. Another alternative is to utilize the do-notation like so: {% highlight coffeescript %} notify = -> alert "Hello, user!" do notify if condition {% endhighlight %} This compiles to the following JavaScript: {% highlight javascript %} var notify; notify = function() { return alert("Hello, user!"); }; if (condition) { notify(); } {% endhighlight %} ## Discussion Like Ruby, CoffeeScript allows you to drop parentheses to method calls. Unlike Ruby, however, CoffeeScript treats a bare function name as the pointer to the function. The practical upshot of this is that if you give no arguments to a method, CoffeeScript cannot tell if you want to call the function or use it as a reference. Is this good or bad? It's just different. It creates an unexpected syntax case -- parentheses aren't _always_ optional -- but in exchange it gives you the ability to pass and receive functions fluently by name, something that's a bit clunky in Ruby. This usage of the do-notation is a neat approach for CoffeeScript with parenphobia. Some people simply prefer to write out the parentheses in the function call, though. ================================================ FILE: chapters/functions/recursion.md ================================================ --- layout: recipe title: Recursive Functions chapter: Functions --- ## Problem You want to call a function from within that same function. ## Solution With a named function: {% highlight coffeescript %} ping = -> console.log "Pinged" setTimeout ping, 1000 {% endhighlight %} With an unnamed function, using @arguments.callee@: {% highlight coffeescript %} delay = 1000 setTimeout((-> console.log "Pinged" setTimeout arguments.callee, delay ), delay) {% endhighlight %} ## Discussion While `arguments.callee` allows for the recursion of anonymous functions and might have the advantage in a very memory-intensive application, named functions keep their purpose more explicit and make for more maintainable code. ================================================ FILE: chapters/functions/splat_arguments.md ================================================ --- layout: recipe title: Splat Arguments chapter: Functions --- ## Problem Your function will be called with a varying number of arguments. ## Solution Use _splats_. {% highlight coffeescript %} loadTruck = (firstDibs, secondDibs, tooSlow...) -> truck: driversSeat: firstDibs passengerSeat: secondDibs trunkBed: tooSlow loadTruck("Amanda", "Joel") # => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: [] } } loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip") # => { truck: { driversSeat: "Amanda", passengerSeat: "Joel", trunkBed: ["Bob", "Mary", "Phillip"] } } {% endhighlight %} With a trailing argument: {% highlight coffeescript %} loadTruck = (firstDibs, secondDibs, tooSlow..., leftAtHome) -> truck: driversSeat: firstDibs passengerSeat: secondDibs trunkBed: tooSlow taxi: passengerSeat: leftAtHome loadTruck("Amanda", "Joel", "Bob", "Mary", "Phillip", "Austin") # => { truck: { driversSeat: 'Amanda', passengerSeat: 'Joel', trunkBed: [ 'Bob', 'Mary', 'Phillip' ] }, taxi: { passengerSeat: 'Austin' } } loadTruck("Amanda") # => { truck: { driversSeat: "Amanda", passengerSeat: undefined, trunkBed: [] }, taxi: undefined } {% endhighlight %} ## Discussion By adding an ellipsis (`...`) next to no more than one of a function's arguments, CoffeeScript will combine all of the argument values not captured by other named arguments into a list. It will serve up an empty list even if some of the named arguments were not supplied. ================================================ FILE: chapters/index.html ================================================ --- layout: default title: Cookbook ---
================================================ FILE: chapters/jquery/ajax.md ================================================ --- layout: recipe title: AJAX chapter: jQuery --- ## Problem You want to make AJAX calls using jQuery. ## Solution {% highlight coffeescript %} $ ?= require 'jquery' # For Node.js compatibility $(document).ready -> # Basic Examples $.get '/', (data) -> $('body').append "Successfully got the page." $.post '/', userName: 'John Doe' favoriteFlavor: 'Mint' (data) -> $('body').append "Successfully posted to the page." # Advanced Settings $.ajax '/', type: 'GET' dataType: 'html' error: (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: #{textStatus}" success: (data, textStatus, jqXHR) -> $('body').append "Successful AJAX call: #{data}" {% endhighlight %} jQuery 1.5 and later have added a new, supplemental API for handling different callbacks. {% highlight coffeescript %} request = $.get '/' request.success (data) -> $('body').append "Successfully got the page again." request.error (jqXHR, textStatus, errorThrown) -> $('body').append "AJAX Error: ${textStatus}." {% endhighlight %} ## Discussion The jQuery and $ variables can be used interchangeably. See also [Callback bindings]({{ site.baseurl }}/chapters/jquery/callback-bindings-jquery). ================================================ FILE: chapters/jquery/callback-bindings-jquery.md ================================================ --- layout: recipe title: Callback Bindings # using => instead of -> chapter: jQuery --- ## Problem You want to bind a callback function to an object. ## Solution {% highlight coffeescript %} $ -> class Basket constructor: () -> @products = [] $('.product').click (event) => @add $(event.currentTarget).attr 'id' add: (product) -> @products.push product console.log @products new Basket() {% endhighlight %} ## Discussion By using the fat arrow (`=>`) instead of the normal arrow (`->`) the function gets automatically bound to the object and can access the `@-variable`. ================================================ FILE: chapters/jquery/index.html ================================================ --- layout: chapter title: jQuery chapter: jQuery --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %} ================================================ FILE: chapters/jquery/plugin.md ================================================ --- layout: recipe title: Create a jQuery Plugin chapter: jQuery --- ## Problem You'd like to create jQuery plugin using CoffeeScript ## Solution {% highlight coffeescript %} # Reference jQuery $ = jQuery # Adds plugin object to jQuery $.fn.extend # Change pluginName to your plugin's name. pluginName: (options) -> # Default settings settings = option1: true option2: false debug: false # Merge default settings with options. settings = $.extend settings, options # Simple logger. log = (msg) -> console?.log msg if settings.debug # _Insert magic here._ return @each ()-> log "Preparing magic show." # You can use your settings in here now. log "Option 1 value: #{settings.option1}" {% endhighlight %} ## Discussion Here are a couple of examples of how to use your new plugin. ### JavaScript {% highlight javascript %} $("body").pluginName({ debug: true }); {% endhighlight %} ### CoffeeScript: {% highlight coffeescript %} $("body").pluginName debug: true {% endhighlight %} ================================================ FILE: chapters/math/constants.md ================================================ --- layout: recipe title: Math Constants chapter: Math --- ## Problem You need to use common mathematical constants like pi or e. ## Solution Use Javascript's Math object to provide commonly needed mathematical constants. {% highlight coffeescript %} Math.PI # => 3.141592653589793 # Note: Capitalization matters! This produces no output, it's undefined. Math.Pi # => Math.E # => 2.718281828459045 Math.SQRT2 # => 1.4142135623730951 Math.SQRT1_2 # => 0.7071067811865476 # Natural log of 2. ln(2) Math.LN2 # => 0.6931471805599453 Math.LN10 # => 2.302585092994046 Math.LOG2E # => 1.4426950408889634 Math.LOG10E # => 0.4342944819032518 {% endhighlight %} ## Discussion For another example of how a math constant is used in a real world problem, refer to the [Converting Radians and Degrees]({{ site.baseurl }}/chapters/math/radians-degrees) section of this Math chapter. ================================================ FILE: chapters/math/fast-fibonacci.md ================================================ --- layout: recipe title: Faster Fibonacci Algorithm chapter: Math --- ## Problem You would like to calculate a number N in the Fibonacci sequence but want to do it quickly. ## Solution The following solution (which can still be improved on) was originally talked about on Robin Houston's blog. Here are a few links talking about the algorithm and ways to improve it: * [http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/](http://bosker.wordpress.com/2011/04/29/the-worst-algorithm-in-the-world/) * [http://www.math.rutgers.edu/~erowland/fibonacci](http://www.math.rutgers.edu/~erowland/fibonacci.html) * [http://jsfromhell.com/classes/bignumber](http://jsfromhell.com/classes/bignumber) * [http://www.math.rutgers.edu/~erowland/fibonacci](http://www.math.rutgers.edu/~erowland/fibonacci.html) * [http://bigintegers.blogspot.com/2010/11/square-division-power-square-root](http://bigintegers.blogspot.com/2010/11/square-division-power-square-root.html) * [http://bugs.python.org/issue3451](http://bugs.python.org/issue3451) This code is in gist form here: [https://gist.github.com/1032685](https://gist.github.com/1032685) {% highlight coffeescript %} ### Author: Jason Giedymin http://www.jasongiedymin.com https://github.com/JasonGiedymin This CoffeeScript Javascript Fast Fibonacci code is based on the python code from Robin Houston's blog. See below links. A few things I want to introduce in time are implementations of Newtonian, Burnikel / Ziegler, and Binet's algorithms on top of a Big Number framework. Todo: - https://github.com/substack/node-bigint - BZ and Newton mods. - Timing ### MAXIMUM_JS_FIB_N = 1476 fib_bits = (n) -> #Represent an integer as an array of binary digits. bits = [] while n > 0 [n, bit] = divmodBasic n, 2 bits.push bit bits.reverse() return bits fibFast = (n) -> #Fast Fibonacci if n < 0 console.log "Choose an number >= 0" return [a, b, c] = [1, 0, 1] for bit in fib_bits n if bit [a, b] = [(a+c)*b, b*b + c*c] else [a, b] = [a*a + b*b, (a+c)*b] c = a + b return b divmodNewton = (x, y) -> throw new Error "Method not yet implemented yet." divmodBZ = () -> throw new Error "Method not yet implemented yet." divmodBasic = (x, y) -> ### Absolutely nothing special here. Maybe later versions will be Newtonian or Burnikel / Ziegler _if_ possible... ### return [(q = Math.floor x/y), (r = if x < y then x else x % y)] start = (new Date).getTime(); calc_value = fibFast(MAXIMUM_JS_FIB_N) diff = (new Date).getTime() - start; console.log "[#{calc_value}] took #{diff} ms." {% endhighlight %} ## Discussion Questions? ================================================ FILE: chapters/math/fast-inv-square.md ================================================ --- layout: recipe title: Fast Inverse Square Root chapter: Math --- ## Problem You would like to calculate a the inverse square root of a number [quickly][5]. ## Solution Appearing in the Quake III Arena [source code][1], this strange algorithm uses integer operations along with a 'magic number' to calculate floating point approximation values of inverse square roots. In this CoffeeScript variant I supply the original classic, and newer optimal 32 bit magic numbers found by [Chris Lomont][2]. Also supplied is the 64-bit sized magic number. Another feature included is the ability to alter the level of precision. This is done by controlling the number of iterations for performing [Newton's method][3]. Depending on the machine and level of precision this algorithm may still provide performance increases over the classic. To run this, compile the script with coffee: coffee -c script.coffee Then copy & paste the compiled js code in to the JavaScript console of your browser. Note: You will need a browser which supports [typed-arrays][4]. References: 1. [ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip](ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip) 2. [http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf](http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf) 3. [http://en.wikipedia.org/wiki/Newton%27s_method](http://en.wikipedia.org/wiki/Newton%27s_method) 4. [https://developer.mozilla.org/en/JavaScript_typed_arrays](https://developer.mozilla.org/en/JavaScript_typed_arrays) 5. [http://en.wikipedia.org/wiki/Fast_inverse_square_root](http://en.wikipedia.org/wiki/Fast_inverse_square_root) [1]: ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip "ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip" [2]: http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf "http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf" [3]: http://en.wikipedia.org/wiki/Newton%27s_method "http://en.wikipedia.org/wiki/Newton%27s_method" [4]: https://developer.mozilla.org/en/JavaScript_typed_arrays "https://developer.mozilla.org/en/JavaScript_typed_arrays" [5]: http://en.wikipedia.org/wiki/Fast_inverse_square_root "http://en.wikipedia.org/wiki/Fast_inverse_square_root" This code is in gist form here: [https://gist.github.com/1036533](https://gist.github.com/1036533) {% highlight coffeescript %} ### Author: Jason Giedymin http://www.jasongiedymin.com https://github.com/JasonGiedymin Appearing in the Quake III Arena source code[1], this strange algorithm uses integer operations along with a 'magic number' to calculate floating point approximation values of inverse square roots[5]. In this CoffeeScript variant I supply the original classic, and newer optimal 32 bit magic numbers found by Chris Lomont[2]. Also supplied is the 64-bit sized magic number. Another feature included is the ability to alter the level of precision. This is done by controlling the number of iterations for performing Newton's method[3]. Depending on the machine and level of precision this algorithm may still provide performance increases over the classic. To run this, compile the script with coffee: coffee -c .coffee Then copy & paste the compiled js code in to the JavaScript console of your browser. Note: You will need a browser which supports typed-arrays[4]. References: [1] ftp://ftp.idsoftware.com/idstuff/source/quake3-1.32b-source.zip [2] http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf [3] http://en.wikipedia.org/wiki/Newton%27s_method [4] https://developer.mozilla.org/en/JavaScript_typed_arrays [5] http://en.wikipedia.org/wiki/Fast_inverse_square_root ### approx_const_quake_32 = 0x5f3759df # See [1] approx_const_32 = 0x5f375a86 # See [2] approx_const_64 = 0x5fe6eb50c7aa19f9 # See [2] fastInvSqrt_typed = (n, precision=1) -> # Using typed arrays. Right now only works in browsers. # Node.JS version coming soon. y = new Float32Array(1) i = new Int32Array(y.buffer) y[0] = n i[0] = 0x5f375a86 - (i[0] >> 1) for iter in [1...precision] y[0] = y[0] * (1.5 - ((n * 0.5) * y[0] * y[0])) return y[0] ### Sample single runs ### testSingle = () -> example_n = 10 console.log("Fast InvSqrt of 10, precision 1: #{fastInvSqrt_typed(example_n)}") console.log("Fast InvSqrt of 10, precision 5: #{fastInvSqrt_typed(example_n, 5)}") console.log("Fast InvSqrt of 10, precision 10: #{fastInvSqrt_typed(example_n, 10)}") console.log("Fast InvSqrt of 10, precision 20: #{fastInvSqrt_typed(example_n, 20)}") console.log("Classic of 10: #{1.0 / Math.sqrt(example_n)}") testSingle() {% endhighlight %} ## Discussion Questions? ================================================ FILE: chapters/math/generating-predictable-random-numbers.md ================================================ --- layout: recipe title: Generating Predictable Random Numbers chapter: Math --- ## Problem You need to generate a random number in a certain range, but you also need to be able to "seed" the generator to deliver predictable values. ## Solution Write your own random number generator. There are a LOT of ways to do this. Here's a simple one. _This generator is +ABSOLUTELY NOT+ acceptable for cryptographic purposes!_ {% highlight coffeescript %} class Rand # if created without a seed, uses current time as seed constructor: (@seed) -> # Knuth and Lewis' improvements to Park and Miller's LCPRNG @multiplier = 1664525 @modulo = 4294967296 # 2**32-1; @offset = 1013904223 unless @seed? && 0 <= seed < @modulo @seed = (new Date().valueOf() * new Date().getMilliseconds()) % @modulo # sets new seed value seed: (seed) -> @seed = seed # return a random integer 0 <= n < @modulo randn: -> # new_seed = (a * seed + c) % m @seed = (@multiplier*@seed + @offset) % @modulo # return a random float 0 <= f < 1.0 randf: -> this.randn() / @modulo # return a random int 0 <= f < n rand: (n) -> Math.floor(this.randf() * n) # return a random int min <= f < max rand2: (min, max) -> min + this.rand(max-min) {% endhighlight %} ## Discussion JavaScript and CoffeeScript do not provide a seedable random number generator. Writing your own will be an exercise in trading off the amount of randomness with the simplicity of the generator. A full discussion of randomness is beyond the scope of this cookbook; for further reading consult Donald Knuth's _The Art of Computer Programming_, Volume II, Chapter 3, "Random Numbers", and _Numerical Recipes in C_, 2nd Edition, Chapter 7, "Random Numbers". A brief explanation of this random number generator is in order, however. It is a Linear Congruential Pseudorandom Number Generator. LCPRNG's operate on the mathematical formula `Ij+1 = (aIj+c) % m`, where a is the multiplier, c is the addition offset, and m is the modulus. Each time a random number is requested, a very large multiplication and addition are performed -- "very large" relative to the key space -- and the resulting number is modulused back down into the keyspace. This generator has a period of 232. It is absolutely unacceptable for cryptographic purposes, but for most simple randomness requirements it is quite adequate. `randn()` will traverse the entire keyspace before repeating itself, and the next number is determined by the previous one. If you want to tinker with this generator, you are _strongly_ encouraged to read Chapter 3 of Knuth's _The Art of Computer Programming_. Random number generation is VERY easy to screw up, and Knuth explains how to tell a good RNG from a bad one. Avoid the temptation to modulus the output of this generator. If you need an integer range, use division. Linear Congruential generators are very nonrandom in their lower bits. This one in particular always generates an odd number from an even seed, and vice versa. So if you need a random 0 or 1, do NOT use {% highlight coffeescript %} # NOT random! Do not do this! r.randn() % 2 {% endhighlight %} because you will most definitely not get random digits. Use `r.rand(2)` instead. ================================================ FILE: chapters/math/generating-random-numbers.md ================================================ --- layout: recipe title: Generating Random Numbers chapter: Math --- ## Problem You need to generate a random number in a certain range. ## Solution Use JavaScript's Math.random() to get floating-point numbers from 0 <= x < 1.0. Use multiplication and Math.floor to get a number in a certain range. {% highlight coffeescript %} probability = Math.random() 0.0 <= probability < 1.0 # => true # Note that percentile does NOT ever reach 100. A full range of 0 to 100 is actually a span of 101. percentile = Math.floor(Math.random() * 100) 0 <= percentile < 100 # => true dice = Math.floor(Math.random() * 6) + 1 1 <= dice <= 6 # => true max = 42 min = -13 range = Math.random() * (max - min) + min -13 <= range < 42 # => true {% endhighlight %} ## Discussion This is a straight lift from JavaScript. Note that JavaScript's Math.random() does not allow you to seed the random number generator to force certain values. See [Generating Predictable Random Numbers]({{ site.baseurl }}/chapters/math/generating-predictable-random-numbers) for that. To generate a number from 0 up to (but not including) n, multiply by n. To generate a number from 1 to n (inclusive), multiply by n and add 1. ================================================ FILE: chapters/math/index.html ================================================ --- layout: chapter title: Math chapter: Math --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %}
================================================ FILE: chapters/math/radians-degrees.md ================================================ --- layout: recipe title: Converting Radians and Degrees chapter: Math --- ## Problem You need to convert between radians and degrees. ## Solution Use Javascript's Math.PI and a simple formula to convert between the two. {% highlight coffeescript %} # To convert from radians to degrees radiansToDegrees = (radians) -> degrees = radians * 180 / Math.PI radiansToDegrees(1) # => 57.29577951308232 # To convert from degrees to radians degreesToRadians = (degrees) -> radians = degrees * Math.PI / 180 degreesToRadians(1) # => 0.017453292519943295 {% endhighlight %} ## Discussion Questions? ================================================ FILE: chapters/math/random-integer.md ================================================ --- layout: recipe title: A Random Integer Function chapter: Math --- ## Problem You'd like to get a random integer between two integers, inclusive. ## Solution Use the following function. {% highlight coffeescript %} randomInt = (lower, upper) -> [lower, upper] = [0, lower] unless upper? # Called with one argument [lower, upper] = [upper, lower] if lower > upper # Lower must be less then upper Math.floor(Math.random() * (upper - lower + 1) + lower) # Last statement is a return value (randomInt(1) for i in [0...10]) # => [0,1,1,0,0,0,1,1,1,0] (randomInt(1, 10) for i in [0...10]) # => [7,3,9,1,8,5,4,10,10,8] {% endhighlight %} ## Discussion Questions? ================================================ FILE: chapters/math/working-with-exponents-and-logarithms.md ================================================ --- layout: recipe title: Working with Exponents and Logarithms chapter: Math --- ## Problem You need to do some calculations that involve exponents and logarithms. ## Solution Use Javascript's Math object to provide common mathematical functions. {% highlight coffeescript %} # Math.pow(x, y) returns x^y Math.pow(2, 4) # => 16 # Math.exp(x) returns E^x and is shorthand for Math.pow(Math.E, x) Math.exp(2) # => 7.38905609893065 # Math.log returns the natural (base E) log Math.log(5) # => 1.6094379124341003 Math.log(Math.exp(42)) # => 42 # To get a log with some other base n, divide by Math.log(n) Math.log(100) / Math.log(10) # => 2 {% endhighlight %} ## Discussion For more information on the Math object see the documentation on the [Mozilla Developer Network](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math). Also refer to [Math Constants]({{ site.baseurl }}/chapters/math/constants) for discussion of the various constants in the Math object. ================================================ FILE: chapters/metaprogramming/detecting-and-replacing-functions.md ================================================ --- layout: recipe title: Detecting and Creating Missing Functions chapter: Metaprogramming --- ## Problem You want to detect if a function exists and create it if it does not (such as an ECMAScript 5 function in Internet Explorer 8). ## Solution Use the existential assignment operator (`?=`) to assign a function to the classes' prototype (using the `::` shorthand), and wrap it all in a IIFE (`do ->`) to contain the variables. {% highlight coffeescript %} do -> Array::filter ?= (callback) -> element for element in this when callback element array = [1..10] array.filter (x) -> x > 5 # => [6,7,8,9,10] {% endhighlight %} ## Discussion Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In Coffeescript, you access this prototype using the `::` shortcut. So, if you want to add a filter function to the array class, you do `Array::filter = ...`. This will add the filter function to all arrays. However, we don't ever want to overwrite a prototype that we haven't created in the first place. For example, if `Array::filter` already exists in a fast native form in the browser, or a library maker has their own specific version of `Array::filter`, then you'll either replace the quick native version with a slow Javascript version, or you will break the library that depends on their own Array::shuffle. What you need to do is only add the function if it doesn't already exist. That's where the existential assignment operator (`?=`) comes in. If we do `Array::filter ?= ...` instead, it will see if `Array::filter` already exists. If it does, then it will use the current version. If it doesn't, it will add yours. Finally, because the existential assignment operator--when compiled--creates a few variables, we clean up the code by wrapping it in an [Immediately-Invoked Function Expression (IIFE)](http://benalman.com/news/2010/11/immediately-invoked-function-expression/). This hides those internal-use-only variables from leaking outside. So, if the function we're writing already exists, it runs, does basically nothing, and exits, affecting absolutely none of your code. But, if the function we're writing *doesn't* exist, we send out only the function we're writing as a closure, so only the function you've made affects the code. The internal workings of `?=` are hidden either way. ### Example Below, we've compiled and annotated the coffeescript written in the solution above {% highlight javascript %} // (function(){ ... })() is an IIFE, compiled in thanks to `do ->` (function() { // This is from the `?=` operator, used to check if Array.prototype.filter (`Array::filter`) exists. // If it does, we set it to itself, and return. If it doesn't, then we set it to the function, and return the function. // The IIFE is only used to hide _base and _ref from the outside world. var _base, _ref; return (_ref = (_base = Array.prototype).filter) != null ? _ref : _base.filter = function(callback) { // `element for element in this when callback element` var element, _i, _len, _results; _results = []; for (_i = 0, _len = this.length; _i < _len; _i++) { element = this[_i]; if (callback(element)) { _results.push(element); } } return _results; }; // The end of the IIFE from `do ->` })(); {% endhighlight %} ================================================ FILE: chapters/metaprogramming/extending-built-in-objects.md ================================================ --- layout: recipe title: Extending Built-in Objects chapter: Metaprogramming --- ## Problem You want to extend a class to add new functionality or replace old. ## Solution Use `::` to assign your new function to the prototype of the object or class. {% highlight coffeescript %} String::capitalize = () -> (this.split(/\s+/).map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' ' "foo bar baz".capitalize() # => 'Foo Bar Baz' {% endhighlight %} ## Discussion Objects in JavaScript (and thus, in CoffeeScript) have a prototype member that defines what member functions should be available on all objects based on that prototype. In CoffeeScript, you can access the prototype directly via the `::` operator. **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-native-builtins/)). ================================================ FILE: chapters/metaprogramming/index.html ================================================ --- layout: chapter title: Metaprogramming chapter: Metaprogramming --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %}
================================================ FILE: chapters/networking/basic-client.md ================================================ --- layout: recipe title: Basic Client chapter: Networking --- ## Problem You want to access a service provided over the network. ## Solution Create a basic TCP client. ### In Node.js {% highlight coffeescript %} net = require 'net' domain = 'localhost' port = 9001 connection = net.createConnection port, domain connection.on 'connect', () -> console.log "Opened connection to #{domain}:#{port}." connection.on 'data', (data) -> console.log "Received: #{data}" connection.end() {% endhighlight %} ### Example Usage Accessing the [Basic Server]({{ site.baseurl }}/chapters/networking/basic-server): {% highlight console %} $ coffee basic-client.coffee Opened connection to localhost:9001 Received: Hello, World! {% endhighlight %} ## Discussion The most important work takes place in the _connection.on 'data'_ handler, where the client receives its response from the server and would most likely arrange for responses to it. See also the [Basic Server]({{ site.baseurl }}/chapters/networking/basic-server), [Bi-Directional Client]({{ site.baseurl }}/chapters/networking/bi-directional-client), and [Bi-Directional Server]({{ site.baseurl }}/chapters/networking/bi-directional-server) recipes. ### Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. ================================================ FILE: chapters/networking/basic-http-client.md ================================================ --- layout: recipe title: Basic HTTP Client chapter: Networking --- ## Problem You want to create a HTTP client. ## Solution In this recipe, we'll use [node.js](http://nodejs.org/)'s HTTP library. We'll go from a simple GET request example to a client which returns the external IP of a computer. ### GET something {% highlight coffeescript %} http = require 'http' http.get { host: 'www.google.com' }, (res) -> console.log res.statusCode {% endhighlight %} The `get` function, from node.js's `http` module, issues a GET request to a HTTP server. The response comes in the form of a callback, which we can handle in a function. This example merely prints the response status code. Check it out: {% highlight console %} $ coffee http-client.coffee 200 {% endhighlight %} ### What's my IP? If you are inside a network which relies on [NAT](http://en.wikipedia.org/wiki/Network_address_translation) such as a LAN, you probably have faced the issue of finding out what's your external IP address. Let's write a small coffeescript for this. {% highlight coffeescript %} http = require 'http' http.get { host: 'checkip.dyndns.org' }, (res) -> data = '' res.on 'data', (chunk) -> data += chunk.toString() res.on 'end', () -> console.log data.match(/([0-9]+\.){3}[0-9]+/)[0] {% endhighlight %} We can get the data from the result object by listening on its `'data'` event; and know that it has come to an end once the `'end'` event has been fired. When that happens, we can do a simple regular expression match to extract our IP address. Try it: {% highlight console %} $ coffee http-client.coffee 123.123.123.123 {% endhighlight %} ## Discussion Note that `http.get` is a shortcut of `http.request`. The latter allows you to issue HTTP requests with different methods, such as POST or PUT. For API and overall information on this subject, check node.js's [http](http://nodejs.org/docs/latest/api/http.html) and [https](http://nodejs.org/docs/latest/api/https.html) documentation pages. Also, the [HTTP spec](http://www.ietf.org/rfc/rfc2616.txt) might come in handy. ### Exercises * Create a client for the key-value store HTTP server, from the [Basic HTTP Server](basic-http-server) recipe. ================================================ FILE: chapters/networking/basic-http-server.md ================================================ --- layout: recipe title: Basic HTTP Server chapter: Networking --- ## Problem You want to create a HTTP server over a network. Over the course of this recipe, we'll go step by step from the smallest server possible to a functional key-value store. ## Solution We'll use [node.js](http://nodejs.org/)'s HTTP library to our own selfish purposes and create the simplest web server possible in Coffeescript. ### Say 'hi\n' We can start by importing node.js's HTTP module. This contains `createServer` which, given a simple request handler, returns a HTTP server. We can use that server to listen on a TCP port. {% highlight coffeescript %} http = require 'http' server = http.createServer (req, res) -> res.end 'hi\n' server.listen 8000 {% endhighlight %} To run this example, simply put in a file and run it. You can kill it with `Ctrl-C`. We can test it using the `curl` command, available on most \*nix platforms: {% highlight console %} $ curl -D - http://localhost:8000/ HTTP/1.1 200 OK Connection: keep-alive Transfer-Encoding: chunked hi {% endhighlight %} ### What's going on? Let's get a little bit more feedback on what's happening on our server. While we're at it, we could also be friendlier to our clients and provide them some HTTP headers. {% highlight coffeescript %} http = require 'http' server = http.createServer (req, res) -> console.log req.method, req.url data = 'hi\n' res.writeHead 200, 'Content-Type': 'text/plain' 'Content-Length': data.length res.end data server.listen 8000 {% endhighlight %} Try to access it once again, but this time use different URL paths, such as `http://localhost:8000/coffee`. You'll see something like this on the server console: {% highlight console %} $ coffee http-server.coffee GET / GET /coffee GET /user/1337 {% endhighlight %} ### GETting stuff What if our webserver was able to hold some data? We'll try to come up with a simple key-value store in which elements are retrievable via GET requests. Provide a key on the request path and the server will return the corresponding value — or 404 if it doesn't exist. {% highlight coffeescript %} http = require 'http' store = # we'll use a simple object as our store foo: 'bar' coffee: 'script' server = http.createServer (req, res) -> console.log req.method, req.url value = store[req.url[1..]] if not value res.writeHead 404 else res.writeHead 200, 'Content-Type': 'text/plain' 'Content-Length': value.length + 1 res.write value + '\n' res.end() server.listen 8000 {% endhighlight %} We can try several URLs to see how it responds: {% highlight console %} $ curl -D - http://localhost:8000/coffee HTTP/1.1 200 OK Content-Type: text/plain Content-Length: 7 Connection: keep-alive script $ curl -D - http://localhost:8000/oops HTTP/1.1 404 Not Found Connection: keep-alive Transfer-Encoding: chunked {% endhighlight %} ### Use your head(ers) Let's face it, `text/plain` is kind of lame. How about if we use something hip like `application/json` or `text/xml`? Also, our store retrieval process could use a bit of refactoring — how about some exception throwing & handling? Let's see what we can come up with: {% highlight coffeescript %} http = require 'http' # known mime types [any, json, xml] = ['*/*', 'application/json', 'text/xml'] # gets a value from the db in format [value, contentType] get = (store, key, format) -> value = store[key] throw 'Unknown key' if not value switch format when any, json then [JSON.stringify({ key: key, value: value }), json] when xml then ["#{ key }\n#{ value }", xml] else throw 'Unknown format' store = foo: 'bar' coffee: 'script' server = http.createServer (req, res) -> console.log req.method, req.url try key = req.url[1..] [value, contentType] = get store, key, req.headers.accept code = 200 catch error contentType = 'text/plain' value = error code = 404 res.writeHead code, 'Content-Type': contentType 'Content-Length': value.length + 1 res.write value + '\n' res.end() server.listen 8000 {% endhighlight %} This server will still return the value which matches a given key, or 404 if non-existent. But it will structure the response either in JSON or XML, according to the `Accept` header. See for yourself: {% highlight console %} $ curl http://localhost:8000/ Unknown key $ curl http://localhost:8000/coffee {"key":"coffee","value":"script"} $ curl -H "Accept: text/xml" http://localhost:8000/coffee coffee script $ curl -H "Accept: image/png" http://localhost:8000/coffee Unknown format {% endhighlight %} ### You gotta give to get back The obvious last step in our adventure is to provide the client the ability to store data. We'll keep our RESTiness by listening to POST requests for this purpose. {% highlight coffeescript %} http = require 'http' # known mime types [any, json, xml] = ['*/*', 'application/json', 'text/xml'] # gets a value from the db in format [value, contentType] get = (store, key, format) -> value = store[key] throw 'Unknown key' if not value switch format when any, json then [JSON.stringify({ key: key, value: value }), json] when xml then ["#{ key }\n#{ value }", xml] else throw 'Unknown format' # puts a value in the db put = (store, key, value) -> throw 'Invalid key' if not key or key is '' store[key] = value store = foo: 'bar' coffee: 'script' # helper function that responds to the client respond = (res, code, contentType, data) -> res.writeHead code, 'Content-Type': contentType 'Content-Length': data.length res.write data res.end() server = http.createServer (req, res) -> console.log req.method, req.url key = req.url[1..] contentType = 'text/plain' code = 404 switch req.method when 'GET' try [value, contentType] = get store, key, req.headers.accept code = 200 catch error value = error respond res, code, contentType, value + '\n' when 'POST' value = '' req.on 'data', (chunk) -> value += chunk req.on 'end', () -> try put store, key, value value = '' code = 200 catch error value = error + '\n' respond res, code, contentType, value server.listen 8000 {% endhighlight %} Notice how the data is received in a POST request. By attaching some handlers on the `'data'` and `'end'` events of the request object, we're able to buffer and finally save the data from the client in the `store`. {% highlight console %} $ curl -D - http://localhost:8000/cookie HTTP/1.1 404 Not Found # ... Unknown key $ curl -D - -d "monster" http://localhost:8000/cookie HTTP/1.1 200 OK # ... $ curl -D - http://localhost:8000/cookie HTTP/1.1 200 OK # ... {"key":"cookie","value":"monster"} {% endhighlight %} ## Discussion Give `http.createServer` a function in the shape of `(request, response) -> ...` and it will return a server object, which we can use to listen on a port. Interact with the `request` and `response` objects to give the server its behaviour. Listen on port 8000 using `server.listen 8000`. For API and overall information on this subject, check node.js's [http](http://nodejs.org/docs/latest/api/http.html) and [https](http://nodejs.org/docs/latest/api/https.html) documentation pages. Also, the [HTTP spec](http://www.ietf.org/rfc/rfc2616.txt) might come in handy. ### Exercises * Create a layer in between the server and the developer which would allow the developer to do something like: {% highlight coffeescript %} server = layer.createServer 'GET /': (req, res) -> ... 'GET /page': (req, res) -> ... 'PUT /image': (req, res) -> ... {% endhighlight %} ================================================ FILE: chapters/networking/basic-server.md ================================================ --- layout: recipe title: Basic Server chapter: Networking --- ## Problem You want to provide a service over a network. ## Solution Create a basic TCP server. ### In Node.js {% highlight coffeescript %} net = require 'net' domain = 'localhost' port = 9001 server = net.createServer (socket) -> console.log "Received connection from #{socket.remoteAddress}" socket.write "Hello, World!\n" socket.end() console.log "Listening to #{domain}:#{port}" server.listen port, domain {% endhighlight %} ### Example Usage Accessed by the [Basic Client]({{ site.baseurl }}/chapters/networking/basic-client): {% highlight console %} $ coffee basic-server.coffee Listening to localhost:9001 Received connection from 127.0.0.1 Received connection from 127.0.0.1 [...] {% endhighlight %} ## Discussion The function passed to @net.createServer@ receives the new socket provided for each new connection to a client. This basic server simply socializes with its visitors but a hard-working server would pass this socket along to a dedicated handler and then return to the task of waiting for the next client. See also the [Basic Client]({{ site.baseurl }}/chapters/networking/basic-client), [Bi-Directional Server]({{ site.baseurl }}/chapters/networking/bi-directional-server), and [Bi-Directional Client]({{ site.baseurl }}/chapters/networking/bi-directional-client) recipes. ### Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. ================================================ FILE: chapters/networking/bi-directional-client.md ================================================ --- layout: recipe title: Bi-Directional Client chapter: Networking --- ## Problem You want to a persistent service over a network, one which maintains an on-going connection with its clients. ## Solution Create a bi-directional TCP client. ### In Node.js {% highlight coffeescript %} net = require 'net' domain = 'localhost' port = 9001 ping = (socket, delay) -> console.log "Pinging server" socket.write "Ping" nextPing = -> ping(socket, delay) setTimeout nextPing, delay connection = net.createConnection port, domain connection.on 'connect', () -> console.log "Opened connection to #{domain}:#{port}" ping connection, 2000 connection.on 'data', (data) -> console.log "Received: #{data}" connection.on 'end', (data) -> console.log "Connection closed" process.exit() {% endhighlight %} ### Example Usage Accessing the [Bi-Directional Server]({{ site.baseurl }}/chapters/networking/bi-directional-server): {% highlight console %} $ coffee bi-directional-client.coffee Opened connection to localhost:9001 Pinging server Received: You have 0 peers on this server Pinging server Received: You have 0 peers on this server Pinging server Received: You have 1 peer on this server [...] Connection closed {% endhighlight %} ## Discussion This particular example initiates contact with the server and starts the conversation in the @connection.on 'connect'@ handler. The bulk of the work in a real client, however, will lie in the @connection.on 'data'@ handler, which processes output from the server. The @ping@ function only recurses in order to illustrate continuous communication with the server and can be removed from a real client. See also the [Bi-Directional Server]({{ site.baseurl }}/chapters/networking/bi-directional-server), [Basic Client]({{ site.baseurl }}/chapters/networking/basic-client), and [Basic Server]({{ site.baseurl }}/chapters/networking/basic-server) recipes. ### Exercises * Add support for choosing the target domain and port based on command-line arguments or from a configuration file. ================================================ FILE: chapters/networking/bi-directional-server.md ================================================ --- layout: recipe title: Bi-Directional Server chapter: Networking --- ## Problem You want to provide a persistent service over a network, one which maintains an on-going connection with a client. ## Solution Create a bi-directional TCP server. ### In Node.js {% highlight coffeescript %} net = require 'net' domain = 'localhost' port = 9001 server = net.createServer (socket) -> console.log "New connection from #{socket.remoteAddress}" socket.on 'data', (data) -> console.log "#{socket.remoteAddress} sent: #{data}" others = server.connections - 1 socket.write "You have #{others} #{others == 1 and "peer" or "peers"} on this server" console.log "Listening to #{domain}:#{port}" server.listen port, domain {% endhighlight %} ### Example Usage Accessed by the [Bi-Directional Client]({{ site.baseurl }}/chapters/networking/bi-directional-client): {% highlight console %} $ coffee bi-directional-server.coffee Listening to localhost:9001 New connection from 127.0.0.1 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping 127.0.0.1 sent: Ping [...] {% endhighlight %} ## Discussion The bulk of the work lies in the @socket.on 'data'@ handler, which processes all of the input from the client. A real server would likely pass the data onto another function to process it and generate any responses so that the original handler. See also the [Bi-Directional Client]({{ site.baseurl }}/chapters/networking/bi-directional-client), [Basic Client]({{ site.baseurl }}/chapters/networking/basic-client), and [Basic Server]({{ site.baseurl }}/chapters/networking/basic-server) recipes. ### Exercises * Add support for choosing the target domain and port based on command-line arguments or on a configuration file. ================================================ FILE: chapters/networking/index.html ================================================ --- layout: chapter title: Networking chapter: Networking --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %}
================================================ FILE: chapters/regular_expressions/heregexes.md ================================================ --- layout: recipe title: Using Heregexes chapter: Regular Expressions --- ## Problem You need to write a complex regular expression. ## Solution Use Coffeescript's "heregexes" -- extended regular expressions that ignore internal whitespace and can contain comments. {% highlight coffeescript %} pattern = /// ^\(?(\d{3})\)? # Capture area code, ignore optional parens [-\s]?(\d{3}) # Capture prefix, ignore optional dash or space -?(\d{4}) # Capture line-number, ignore optional dash /// [area_code, prefix, line] = "(555)123-4567".match(pattern)[1..3] # => ['555', '123', '4567'] {% endhighlight %} ## Discussion Breaking up your complex regular expressions and commenting key sections makes them a lot more decipherable and maintainable. For example, changing this regex to allow an optional space between the prefix and line number would now be fairly obvious. ### Whitespace characters in heregexes Whitespace is ignored in heregexes -- so what do you do if you need to match a literal ASCII space? One solution is to use the @\s@ character class, which will match spaces, tabs and line breaks. If you only want to match a space, though, you'll need to use `\x20` to denote a literal ASCII space. ================================================ FILE: chapters/regular_expressions/index.html ================================================ --- layout: chapter title: Regular Expressions chapter: Regular Expressions --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %}
================================================ FILE: chapters/regular_expressions/replacing-html-tags-with-html-named-entities.md ================================================ --- layout: recipe title: Replacing HTML Tags with HTML Named Entities chapter: Regular Expressions --- ## Problem You need to replace HTML tags with named entities: `
=> <br/>` ## Solution {% highlight coffeescript %} htmlEncode = (str) -> str.replace /[&<>"']/g, ($0) -> "&" + {"&":"amp", "<":"lt", ">":"gt", '"':"quot", "'":"#39"}[$0] + ";" htmlEncode('Barnes & Noble') # => '<a href="http://bn.com">Barnes & Noble</a>' {% endhighlight %} ## Discussion There are probably better ways to implement the above method. ================================================ FILE: chapters/regular_expressions/replacing-substrings.md ================================================ --- layout: recipe title: Replacing Substrings chapter: Regular Expressions --- ## Problem You need to replace a portion of a string with another value. ## Solution Use the JavaScript `replace` method. `replace` matches with the given string, and returns the edited string. The first version takes 2 arguments: _pattern_ and _string replacement_ {% highlight coffeescript %} "JavaScript is my favorite!".replace /Java/, "Coffee" # => 'CoffeeScript is my favorite!' "foo bar baz".replace /ba./, "foo" # => 'foo foo baz' "foo bar baz".replace /ba./g, "foo" # => 'foo foo foo' {% endhighlight %} The second version takes 2 arguments: _pattern_ and _callback function_ {% highlight coffeescript %} "CoffeeScript is my favorite!".replace /(\w+)/g, (match) -> match.toUpperCase() # => 'COFFEESCRIPT IS MY FAVORITE!' {% endhighlight %} The callback function is invoked for each match, and the match value is passed as the argument to the callback. ## Discussion Regular Expressions are a powerful way to match and replace strings. ================================================ FILE: chapters/regular_expressions/searching-for-substrings.md ================================================ --- layout: recipe title: Searching for Substrings chapter: Regular Expressions --- ## Problem You need to search for a substring, and return either the starting position of the match or the matching value itself. ## Solution There are several ways to accomplish this using regular expressions. Some methods are called on a `RegExp` pattern or object and some are called on `String` objects. ### `RegExp` objects The first way is to call the `test` method on a `RegExp` pattern or object. The `test` method returns a boolean value: {% highlight coffeescript %} match = /sample/.test("Sample text") # => false match = /sample/i.test("Sample text") # => true {% endhighlight %} The next way to is to call the `exec` method on a `RegExp` pattern or object. The `exec` method returns an array with the match information or `null`: {% highlight coffeescript %} match = /s(amp)le/i.exec "Sample text" # => [ 'Sample', 'amp', index: 0, input: 'Sample text' ] match = /s(amp)le/.exec "Sample text" # => null {% endhighlight %} ### `String` objects The `match` method matches a given string with the `RegExp`. With 'g' flag returns an array containing the matches, without 'g' flag returns just the first match or if no match is found returns `null`. {% highlight coffeescript %} "Watch out for the rock!".match(/r?or?/g) # => [ 'o', 'or', 'ro' ] "Watch out for the rock!".match(/r?or?/) # => [ 'o', index: 6, input: 'Watch out for the rock!' ] "Watch out for the rock!".match(/ror/) # => null {% endhighlight %} The `search` method matches `RegExp` with string and returns the index of the beginning of the match if found, -1 if not. {% highlight coffeescript %} "Watch out for the rock!".search /for/ # => 10 "Watch out for the rock!".search /rof/ # => -1 {% endhighlight %} ## Discussion Regular Expressions are a powerful way to test and match substrings. ================================================ FILE: chapters/strings/capitalizing-words.md ================================================ --- layout: recipe title: Capitalizing Words chapter: Strings --- ## Problem You want to capitalize the first letter of every word in a string. ## Solution Use the split, map, join pattern: Split the string into words, then use a map to capitalize the first letter and lowercase all other letters of each word before gluing the string back together with join. {% highlight coffeescript %} ("foo bar baz".split(' ').map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' ' # => 'Foo Bar Baz' {% endhighlight %} Or do the same thing using a list comprehension: {% highlight coffeescript %} (word[0].toUpperCase() + word[1..-1].toLowerCase() for word in "foo bar baz".split /\s+/).join ' ' # => 'Foo Bar Baz' {% endhighlight %} ## Discussion Split, map, join is a common scripting pattern dating back to Perl. This function may benefit from being placed directly onto the String class by [Extending Classes]({{ site.baseurl }}/chapters/objects/extending-classes). Be aware that two wrinkles can appear in the split, map, join pattern. The first is that the split text works best when it is constant. If the source string has multiple spaces in it, the split will need to take this into account to prevent getting extra, empty words. One way to do this is with a regular expression to split on runs of whitespace instead of a single space: {% highlight coffeescript %} ("foo bar baz".split(/\s+/).map (word) -> word[0].toUpperCase() + word[1..-1].toLowerCase()).join ' ' # => 'Foo Bar Baz' {% endhighlight %} ...but this leads us to the second wrinkle: notice that the runs of whitespace are now compressed down to a single character by the join. Quite often one or both of these wrinkles is acceptable, however, so the split, map, join pattern can be a powerful tool. ================================================ FILE: chapters/strings/finding-substrings.md ================================================ --- layout: recipe title: Finding Substrings chapter: Strings --- ## Problem You need to find the first or last occurrence of a search string within a message. ## Solution Use Javascript's indexOf() and lastIndexOf() to find the first and last occurrences of a string, respectively. Syntax: string.indexOf searchstring, start {% highlight coffeescript %} message = "This is a test string. This has a repeat or two. This might even have a third." message.indexOf "This", 0 # => 0 # Modifying the start parameter message.indexOf "This", 5 # => 23 message.lastIndexOf "This" # => 49 # Count occurrences of a given string message.split(" a ").length - 1 # => 3 {% endhighlight %} ================================================ FILE: chapters/strings/generating-a-unique-id.md ================================================ --- layout: recipe title: Generating a Unique ID chapter: Strings --- ## Problem You want to generate a random unique identifier. ## Solution You can create a Base 36 encoded string from a random number. {% highlight coffeescript %} uniqueId = (length=8) -> id = "" id += Math.random().toString(36).substr(2) while id.length < length id.substr 0, length uniqueId() # => n5yjla3b uniqueId(2) # => 0d uniqueId(20) # => ox9eo7rt3ej0pb9kqlke uniqueId(40) # => xu2vo4xjn4g0t3xr74zmndshrqlivn291d584alj {% endhighlight %} ## Discussion There are other possible techniques, but this is relatively performant and flexible. ================================================ FILE: chapters/strings/index.html ================================================ --- layout: chapter title: Strings chapter: Strings --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
    {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
  • {{ page.title }}
  • {% endunless %} {% endif %} {% endfor %}
================================================ FILE: chapters/strings/interpolation.md ================================================ --- layout: recipe title: String Interpolation chapter: Strings --- ## Problem You want to create a string that contains a text representation of a CoffeeScript Variable. ## Solution Use CoffeeScript's ruby-like string interpolation instead of JavaScript's string addition. You must use Double-quoted strings to allow for interpolation. Single-quoted strings are treated as literals. Interpolation: {% highlight coffeescript %} muppet = "Beeker" favorite = "My favorite muppet is #{muppet}!" # => "My favorite muppet is Beeker!" {% endhighlight %} {% highlight coffeescript %} square = (x) -> x * x message = "The square of 7 is #{square 7}." # => "The square of 7 is 49." {% endhighlight %} ## Discussion CoffeeScript interpolates strings in similar fashion to ruby. Most expressions are valid inside the `#{...}` interpolation syntax. CoffeeScript permits multiple expressions inside the interpolation which can have side effects, but this is discouraged. Only the last value will be returned. {% highlight coffeescript %} # You can do this, but don't. YOU WILL GO MAD. square = (x) -> x * x muppet = "Beeker" message = "The square of 10 is #{muppet='Animal'; square 10}. Oh, and your favorite muppet is now #{muppet}." # => "The square of 10 is 100. Oh, and your favorite muppet is now Animal." {% endhighlight %} ================================================ FILE: chapters/strings/lowercasing-a-string.md ================================================ --- layout: recipe title: Lowercasing a String chapter: Strings --- ## Problem You want to lowercase a string. ## Solution Use JavaScript's String toLowerCase() method: {% highlight coffeescript %} "ONE TWO THREE".toLowerCase() # => 'one two three' {% endhighlight %} ## Discussion `toLowerCase()` is a standard JavaScript method. Don't forget the parentheses. ### Syntax Sugar You can add some Ruby-like syntax sugar with the following shortcut: {% highlight coffeescript %} String::downcase = -> @toLowerCase() "ONE TWO THREE".downcase() # => 'one two three' {% endhighlight %} The snippet above demonstrates a few features of CoffeeScript: * The double-colon `::` is shorthand for saying `.prototype.` * The "at" sign `@` is shorthand for saying `this.` The code above compiles in to the following JavaScript: {% highlight javascript %} String.prototype.downcase = function() { return this.toLowerCase(); }; "ONE TWO THREE".downcase(); {% endhighlight %} **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). ================================================ FILE: chapters/strings/matching-strings.md ================================================ --- layout: recipe title: Matching Strings chapter: Strings --- ## Problem You want to match two or more strings. ## Solution Calculate the edit distance, or number of operations required to transform one string into the other. {% highlight coffeescript %} levenshtein = (str1, str2) -> l1 = str1.length l2 = str2.length prevDist = [0..l2] nextDist = [0..l2] for i in [1..l1] by 1 nextDist[0] = i for j in [1..l2] by 1 if (str1.charAt i-1) == (str2.charAt j-1) nextDist[j] = prevDist[j-1] else nextDist[j] = 1 + Math.min prevDist[j], nextDist[j-1], prevDist[j-1] [prevDist,nextDist]=[nextDist, prevDist] prevDist[l2] {% endhighlight %} ## Discussion You can use either Hirschberg or Wagner–Fischer's algorithm to calculate a Levenshtein distance. This example uses Wagner–Fischer's algorithm. This version of Levenshtein algorithm is linear in memory, quadratic in time. str.charAt i is preferred here to str[i] because the latter syntax is not supported by some browsers (e.g. IE7). At first glance the use of "by 1" in the two loops might look useless. It is actually here to avoid a common danger of the coffeescript [i..j] syntax. If str1 or str2 is an empty string, then [1..l1] or [1..l2] will return [1,0]. The loops with the "by 1" statement also compiles to cleaner / slightly more performant javascript. Finally the optimization of recycling of arrays at the end of the loops is mainly here to demonstrate the syntax of coffeescript for swapping two variables. ================================================ FILE: chapters/strings/repeating.md ================================================ --- layout: recipe title: Repeating a String chapter: Strings --- ## Problem You want to repeat a string. ## Solution Create an array of n+1 nulls, and then join it with the repetition string as the glue: {% highlight coffeescript %} # create a string of 10 foos Array(11).join 'foo' # => "foofoofoofoofoofoofoofoofoofoo" {% endhighlight %} ## Repeat method for Strings You could also create a method for this in String prototype. It is as simple as that: {% highlight coffeescript %} # add repeat method for all strings, that returns string repeated n times String::repeat = (n) -> Array(n+1).join(this) {% endhighlight %} ## Discussion JavaScript lacks a string repeat function, as does CoffeeScript. List comprehensions and maps can be pressed into service here, but in the case of a simple string repeat it's easier to simply build an array of n+1 nulls and then glue them together. ================================================ FILE: chapters/strings/replacing-sub-strings.md ================================================ --- layout: recipe title: Replacing Sub-Strings Within a String chapter: Strings --- ## Problem You want to replace a sub-string with a new sub-string. ## Solution Split the string using the sub-string you want to remove as a delimiter. Then re-join using the new sub-string as the delimiter. {% highlight coffeescript %} "Orange is the new Black".split("Orange").join("Pink") # => "Pink is the new Black" "I am so sad. I cannot believe how sad I am today!".split("sad").join("happy") # => "I am so happy. I cannot believe how happy I am today!" "I am not a crook.".split("not ").join("") # => "I am a crook." {% endhighlight %} ## Discussion You can also use regexes. If you're matching an exact string, this way is simpler and 10x faster. If you use regexes, remember that you must escape certain characters. ================================================ FILE: chapters/strings/splitting-a-string.md ================================================ --- layout: recipe title: Splitting a String chapter: Strings --- ## Problem You want to split a string. ## Solution Use JavaScript's String split() method: {% highlight coffeescript %} "foo bar baz".split " " # => [ 'foo', 'bar', 'baz' ] {% endhighlight %} ## Discussion String's split() is a standard JavaScript method. It can be used to split a string on any delimiter, including regular expressions. It also accepts a second parameter that specifies the number of splits to return. {% highlight coffeescript %} "foo-bar-baz".split "-" # => [ 'foo', 'bar', 'baz' ] {% endhighlight %} {% highlight coffeescript %} "foo bar \t baz".split /\s+/ # => [ 'foo', 'bar', 'baz' ] {% endhighlight %} {% highlight coffeescript %} "the sun goes down and I sit on the old broken-down river pier".split " ", 2 # => [ 'the', 'sun' ] {% endhighlight %} ================================================ FILE: chapters/strings/trimming-whitespace-from-a-string.md ================================================ --- layout: recipe title: Trimming Whitespace from a String chapter: Strings --- ## Problem You want to trim whitespace from a string. ## Solution Use JavaScript's Regular Expression support to replace whitespace. To trim leading and trailing whitespace, use the following: {% highlight coffeescript %} " padded string ".replace /^\s+|\s+$/g, "" # => 'padded string' {% endhighlight %} To trim only leading whitespace, use the following: {% highlight coffeescript %} " padded string ".replace /^\s+/g, "" # => 'padded string ' {% endhighlight %} To trim only trailing whitespace, use the following: {% highlight coffeescript %} " padded string ".replace /\s+$/g, "" # => ' padded string' {% endhighlight %} ## Discussion Opera, Firefox and Chrome all have a native string prototype `trim` method, and the other browsers could add one as well. For this particular method, I would use the built-in method where possible, otherwise create a polyfill: {% highlight coffeescript %} unless String::trim then String::trim = -> @replace /^\s+|\s+$/g, "" " padded string ".trim() # => 'padded string' {% endhighlight %} ### Syntax Sugar You can add some Ruby-like syntax sugar with the following shortcuts: {% highlight coffeescript %} String::strip = -> if String::trim? then @trim() else @replace /^\s+|\s+$/g, "" String::lstrip = -> @replace /^\s+/g, "" String::rstrip = -> @replace /\s+$/g, "" " padded string ".strip() # => 'padded string' " padded string ".lstrip() # => 'padded string ' " padded string ".rstrip() # => ' padded string' {% endhighlight %} For an interesting discussion and benchmarks of JavaScript `trim` performance, see [this blog post](http://blog.stevenlevithan.com/archives/faster-trim-javascript) by Steve Levithan. ================================================ FILE: chapters/strings/uppercasing-a-string.md ================================================ --- layout: recipe title: Uppercasing a String chapter: Strings --- ## Problem You want to uppercase a string. ## Solution Use JavaScript's String toUpperCase() method: {% highlight coffeescript %} "one two three".toUpperCase() # => 'ONE TWO THREE' {% endhighlight %} ## Discussion `toUpperCase()` is a standard JavaScript method. Don't forget the parentheses. ### Syntax Sugar You can add some Ruby-like syntax sugar with the following shortcut: {% highlight coffeescript %} String::upcase = -> @toUpperCase() "one two three".upcase() # => 'ONE TWO THREE' {% endhighlight %} The snippet above demonstrates a few features of CoffeeScript: * The double-colon `::` is shorthand for saying `.prototype.` * The "at" sign `@` is shorthand for saying `this.` The code above compiles in to the following JavaScript: {% highlight javascript %} String.prototype.upcase = function() { return this.toUpperCase(); }; "one two three".upcase(); {% endhighlight %} **Note:** Although it's quite common in languages like Ruby, extending native objects is often considered bad practice in JavaScript (see: [Maintainable JavaScript: Don’t modify objects you don’t own](http://www.nczonline.net/blog/2010/03/02/maintainable-javascript-dont-modify-objects-you-down-own/); [Extending built-in native objects. Evil or not?](http://perfectionkills.com/extending-built-in-native-objects-evil-or-not/)). ================================================ FILE: chapters/syntax/code_reuse_on_client_and_server.md ================================================ --- layout: recipe title: Code Reuse on Client and Server chapter: Syntax --- ## Problem You have created some functionality in CoffeeScript that you wish to use on the client with a web browser and on the server with Node.js. ## Solution Export the functionality in the following manner: {% highlight coffeescript %} # simpleMath.coffee # these methods are private add = (a, b) -> a + b subtract = (a, b) -> a - b square = (x) -> x * x # create a namespace to export our public methods SimpleMath = exports? and exports or @SimpleMath = {} # items attached to our namespace are available in Node.js as well as client browsers class SimpleMath.Calculator add: add subtract: subtract square: square {% endhighlight %} ## Discussion In the above example, we create a new namespace called SimpleMath. If `export` is available, our class is exported as a Node.js module. If `export` is *not* available, then SimpleMath is added to the global namespace and available to our web page. In Node.js, we can include our module using the `require` command. {% highlight console %} $ node > var SimpleMath = require('./simpleMath'); undefined > var Calc = new SimpleMath.Calculator(); undefined > console.log("5 + 6 = ", Calc.add(5, 6)); 5 + 6 = 11 undefined > {% endhighlight %} In our web page, we can include our module by including it as a script. {% highlight html %} SimpleMath Module Example

A SimpleMath Example

    {% endhighlight %} Result: #A SimpleMath Example * 5 + 6 = 11 ================================================ FILE: chapters/syntax/comparing_ranges.md ================================================ --- layout: recipe title: Comparing Ranges chapter: Syntax --- ## Problem You want to know if a variable is inside a given range. ## Solution Use CoffeeScript's chained comparison syntax. {% highlight coffeescript %} maxDwarfism = 147 minAcromegaly = 213 height = 180 normalHeight = maxDwarfism < height < minAcromegaly # => true {% endhighlight %} ## Discussion This is a nice feature lifted from Python. Instead of writing out the full comparison like {% highlight coffeescript %} normalHeight = height > maxDwarfism && height < minAcromegaly {% endhighlight %} CoffeeScript allows us to chain the two comparisons together in a form that more closely matches the way a mathematician would write it. ================================================ FILE: chapters/syntax/embedding_javascript.md ================================================ --- layout: recipe title: Embedding JavaScript chapter: Syntax --- ## Problem You want to include some found/pre-written JavaScript code inline with your CoffeeScript. ## Solution Wrap the JavaScript with backticks: {% highlight coffeescript %} `function greet(name) { return "Hello "+name; }` # Back to CoffeeScript greet "Coffee" # => "Hello Coffee" {% endhighlight %} ## Discussion This is a simple way to integrate small snippets of JavaScript code into your CoffeeScript without converting it over to use CoffeeScript syntax. As shown in the [CoffeeScript Language Reference](http://jashkenas.github.com/coffee-script/#embedded) you can mix the two languages to a certain extent: {% highlight coffeescript %} hello = `function (name) { return "Hello "+name }` hello "Coffee" # => "Hello Coffee" {% endhighlight %} Here the `hello` variable is still in CoffeeScript, but is assigned a function written in JavaScript. ================================================ FILE: chapters/syntax/for_loops.md ================================================ --- layout: recipe title: For Loops chapter: Syntax --- ## Problem You need to iterate over an array, object or range with a for loop. ## Solution {% highlight coffeescript %} # for(i = 1; i<= 10; i++) x for x in [1..10] # => [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ] # To count by 2 # for(i=1; i<= 10; i=i+2) x for x in [1..10] by 2 # => [ 1, 3, 5, 7, 9 ] # Perform a simple operation like squaring each item. x * x for x in [1..10] # = > [1,4,9,16,25,36,49,64,81,100] {% endhighlight %} ## Discussion Comprehensions replace for loops in CoffeeScript, but they simply compile into the traditional javascript equivalent for-loop. ================================================ FILE: chapters/syntax/index.html ================================================ --- layout: chapter title: Syntax chapter: Syntax --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %}
    ================================================ FILE: chapters/testing/index.html ================================================ --- layout: chapter title: Testing chapter: Testing --- {% capture url %}/chapters/{{ page.chapter | replace: ' ', '_' | downcase }}{% endcapture %} {% capture indexurl %}{{ url }}/index.html{% endcapture %}
      {% for page in site.pages %} {% if page.url contains url %} {% unless page.url == indexurl %}
    • {{ page.title }}
    • {% endunless %} {% endif %} {% endfor %}
    ================================================ FILE: chapters/testing/testing_with_jasmine.md ================================================ --- layout: recipe title: Testing with Jasmine chapter: Testing --- ## Problem You are writing a simple calculator using CoffeeScript and you want to verify it functions as expected. You decide to use the Jasmine test framework. ## Discussion When using the Jasmine test framework, you write tests in a specification (spec) file that describes the expected functionality of the code to be tested. For example, we expect our calculator will be able to add and subtract and will function correctly with both positive and negative numbers. Our spec is listed below. {% highlight coffeescript %} # calculatorSpec.coffee describe 'Calculator', -> it 'can add two positive numbers', -> calculator = new Calculator() result = calculator.add 2, 3 expect(result).toBe 5 it 'can handle negative number addition', -> calculator = new Calculator() result = calculator.add -10, 5 expect(result).toBe -5 it 'can subtract two positive numbers', -> calculator = new Calculator() result = calculator.subtract 10, 6 expect(result).toBe 4 it 'can handle negative number subtraction', -> calculator = new Calculator() result = calculator.subtract 4, -6 expect(result).toBe 10 {% endhighlight %} ### Configuring Jasmine Before you can run your tests, you must download and configure Jasmine. This involves: 1. downloading the latest Jasmine zip file; 2. creating a spec and a spec/jasmine folder in your project; 3. extracting the downloaded Jasmine files into the spec/jasmine folder; and 4. creating a test runner. ### Create a Test Runner Jasmine can run your tests within a web browser by using a spec runner HTML file. The spec runner is a simple HTML page that links the necessary JavaScript and CSS files for both Jasmine and your code. A sample is below. {% highlight html linenos %} Jasmine Spec Runner {% endhighlight %} This spec runner can be downloaded from this GitHub gist. To use the SpecRunner.html, simply reference your compiled JavaScript files and compiled tests after jasmine.js and its dependencies. In the above example, we include our yet-to-be-developed calculator.js file on line 14 and our compiled calculatorSpec.js file on line 17. ## Running the Tests To run our tests, simply open SpecRunner.html in a web browser. In our example we see 4 failing specs with a total of 8 failures (below). All failing tests It appears our tests are failing because Jasmine cannot find the variable Calculator. That's because it has not been created yet. Let's do that now by creating a new file named js/calculator.coffee. {% highlight coffeescript %} # calculator.coffee window.Calculator = class Calculator {% endhighlight %} Compile calculator.coffee and refresh the browser to re-run the test suite. Still failing, but better We now have 4 failures instead of our previous 8. That's a 50% improvement with only one line of code. ## Getting the Tests to Pass Let's implement our methods and see if we can get these tests to pass. {% highlight coffeescript %} # calculator.coffee window.Calculator = class Calculator add: (a, b) -> a + b subtract: (a, b) -> a - b {% endhighlight %} When we refresh we see they all pass. All passing ## Refactoring the Tests Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our spec file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initialization code into a routine that runs before each test. Luckily Jasmine has a beforeEach function just for this purpose. {% highlight coffeescript %} describe 'Calculator', -> calculator = null beforeEach -> calculator = new Calculator() it 'can add two positive numbers', -> result = calculator.add 2, 3 expect(result).toBe 5 it 'can handle negative number addition', -> result = calculator.add -10, 5 expect(result).toBe -5 it 'can subtract two positive numbers', -> result = calculator.subtract 10, 6 expect(result).toBe 4 it 'can handle negative number subtraction', -> result = calculator.subtract 4, -6 expect(result).toBe 10 {% endhighlight %} When we recompile our spec and refresh the browser we see the tests still all pass. All passing ================================================ FILE: chapters/testing/testing_with_nodeunit.md ================================================ --- layout: recipe title: Testing with Nodeunit chapter: Testing --- ## Problem You are writing a simple calculator using CoffeeScript and you want to verify it functions as expected. You decide to use the Nodeunit test framework. ## Discussion Nodeunit is a JavaScript implementation of the xUnit family of Unit Testing libraries, similar libraries are available for Java, Python, Ruby, Smalltalk etc. When using xUnit family test frameworks, you write tests in a file that describes the expected functionality of the code to be tested. For example, we expect our calculator will be able to add and subtract and will function correctly with both positive and negative numbers. Our test is listed below. {% highlight coffeescript %} # test/calculator.test.coffee Calculator = require '../calculator' exports.CalculatorTest = 'test can add two positive numbers': (test) -> calculator = new Calculator result = calculator.add 2, 3 test.equal(result, 5) test.done() 'test can handle negative number addition': (test) -> calculator = new Calculator result = calculator.add -10, 5 test.equal(result, -5) test.done() 'test can subtract two positive numbers': (test) -> calculator = new Calculator result = calculator.subtract 10, 6 test.equal(result, 4) test.done() 'test can handle negative number subtraction': (test) -> calculator = new Calculator result = calculator.subtract 4, -6 test.equal(result, 10) test.done() {% endhighlight %} ### Installing Nodeunit Before you can run your tests, you must install Nodeunit: First of all create a `package.json` file {% highlight javascript %} { "name": "calculator", "version": "0.0.1", "scripts": { "test": "./node_modules/.bin/nodeunit test" }, "dependencies": { "coffee-script": "~1.4.0", "nodeunit": "~0.7.4" } } {% endhighlight %} Next from a terminal run. {% highlight bash %} $ npm install {% endhighlight %} ## Running the Tests It's easy to run the tests from the command-line: {% highlight bash %} $ npm test {% endhighlight %} The test runner should fail, because we have no calculator.coffee suki@Yuzuki:nodeunit_testing (master)$ npm test npm WARN package.json calculator@0.0.1 No README.md file found! > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing > ./node_modules/.bin/nodeunit test /Users/suki/tmp/nodeunit_testing/node_modules/nodeunit/lib/nodeunit.js:72 if (err) throw err; ^ Error: ENOENT, stat '/Users/suki/tmp/nodeunit_testing/test' npm ERR! Test failed. See above for more details. npm ERR! not ok code 0 Let's create a simple file {% highlight coffeescript %} # calculator.coffee class Calculator module.exports = Calculator {% endhighlight %} And re-run the test suite. suki@Yuzuki:nodeunit_testing (master)$ npm test npm WARN package.json calculator@0.0.1 No README.md file found! > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing > ./node_modules/.bin/nodeunit test calculator.test ✖ CalculatorTest - test can add two positive numbers TypeError: Object # has no method 'add' ... ✖ CalculatorTest - test can handle negative number addition TypeError: Object # has no method 'add' ... ✖ CalculatorTest - test can subtract two positive numbers TypeError: Object # has no method 'subtract' ... ✖ CalculatorTest - test can handle negative number subtraction TypeError: Object # has no method 'subtract' ... FAILURES: 4/4 assertions failed (31ms) npm ERR! Test failed. See above for more details. npm ERR! not ok code 0 ## Getting the Tests to Pass Let's implement our methods and see if we can get these tests to pass. {% highlight coffeescript %} # calculator.coffee class Calculator add: (a, b) -> a + b subtract: (a, b) -> a - b module.exports = Calculator {% endhighlight %} When we rerun the tests we see they're all passing: suki@Yuzuki:nodeunit_testing (master)$ npm test npm WARN package.json calculator@0.0.1 No README.md file found! > calculator@0.0.1 test /Users/suki/tmp/nodeunit_testing > ./node_modules/.bin/nodeunit test calculator.test ✔ CalculatorTest - test can add two positive numbers ✔ CalculatorTest - test can handle negative number addition ✔ CalculatorTest - test can subtract two positive numbers ✔ CalculatorTest - test can handle negative number subtraction OK: 4 assertions (27ms) ## Refactoring the Tests Now that our tests pass, we should look to see if our code or our test(s) can be refactored. In our test file, each test creates its own calculator instance. This can make our tests quite repetitive especially for larger test suites. Ideally, we should consider moving that initialization code into a routine that runs before each test. In common with other xUnit libraries, Nodeunit provides a setUp (and tearDown) function which will be called before each test. {% highlight coffeescript %} Calculator = require '../calculator' exports.CalculatorTest = setUp: (callback) -> @calculator = new Calculator callback() 'test can add two positive numbers': (test) -> result = @calculator.add 2, 3 test.equal(result, 5) test.done() 'test can handle negative number addition': (test) -> result = @calculator.add -10, 5 test.equal(result, -5) test.done() 'test can subtract two positive numbers': (test) -> result = @calculator.subtract 10, 6 test.equal(result, 4) test.done() 'test can handle negative number subtraction': (test) -> result = @calculator.subtract 4, -6 test.equal(result, 10) test.done() {% endhighlight %} We can rerun the tests and everything should continue to pass. ================================================ FILE: contributing.md ================================================ --- layout: default title: Contributing --- # Contributing The Cookbook needs your help! Here's the Contribution Recipe: 1. Fork the repository at [https://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com](https://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com) 2. Do awesomeness! 3. Send a pull request to coffeescript-cookbook 4. If we merge your pull request, you get commit access. BAM. Go back to step 2 and stay there as long as you want. ## Wanted Recipes Want to help, but don't know where to start? Want a recipe, but don't know how to write it? Check out the [Wanted Recipes]({{ site.baseurl }}/wanted-recipes) page! ## Authors Write recipes! Fork the repository, author some pages, and send us a pull request. For more information read the [Author's Guide]({{ site.baseurl }}/authors-guide). ## Developers Improve the code! Fork the repository, extend or improve the site coded, and send a pull request. For more information read the [Developer's Guide]({{ site.baseurl }}/developers-guide). ## Designers Make this site look pretty! Fork the repository, extend, improve or update the design, and send a pull request. For more information read the [Designer's Guide]({{ site.baseurl }}/designers-guide). ================================================ FILE: css/autumn.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #aaaaaa; font-style: italic } /* Comment */ .highlight .err { color: #F00000; background-color: #F0A0A0 } /* Error */ .highlight .k { color: #0000aa } /* Keyword */ .highlight .cm { color: #aaaaaa; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #4c8317 } /* Comment.Preproc */ .highlight .c1 { color: #aaaaaa; font-style: italic } /* Comment.Single */ .highlight .cs { color: #0000aa; font-style: italic } /* Comment.Special */ .highlight .gd { color: #aa0000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00aa00 } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #0000aa } /* Keyword.Constant */ .highlight .kd { color: #0000aa } /* Keyword.Declaration */ .highlight .kn { color: #0000aa } /* Keyword.Namespace */ .highlight .kp { color: #0000aa } /* Keyword.Pseudo */ .highlight .kr { color: #0000aa } /* Keyword.Reserved */ .highlight .kt { color: #00aaaa } /* Keyword.Type */ .highlight .m { color: #009999 } /* Literal.Number */ .highlight .s { color: #aa5500 } /* Literal.String */ .highlight .na { color: #1e90ff } /* Name.Attribute */ .highlight .nb { color: #00aaaa } /* Name.Builtin */ .highlight .nc { color: #00aa00; text-decoration: underline } /* Name.Class */ .highlight .no { color: #aa0000 } /* Name.Constant */ .highlight .nd { color: #888888 } /* Name.Decorator */ .highlight .ni { color: #800000; font-weight: bold } /* Name.Entity */ .highlight .nf { color: #00aa00 } /* Name.Function */ .highlight .nn { color: #00aaaa; text-decoration: underline } /* Name.Namespace */ .highlight .nt { color: #1e90ff; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #aa0000 } /* Name.Variable */ .highlight .ow { color: #0000aa } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #009999 } /* Literal.Number.Float */ .highlight .mh { color: #009999 } /* Literal.Number.Hex */ .highlight .mi { color: #009999 } /* Literal.Number.Integer */ .highlight .mo { color: #009999 } /* Literal.Number.Oct */ .highlight .sb { color: #aa5500 } /* Literal.String.Backtick */ .highlight .sc { color: #aa5500 } /* Literal.String.Char */ .highlight .sd { color: #aa5500 } /* Literal.String.Doc */ .highlight .s2 { color: #aa5500 } /* Literal.String.Double */ .highlight .se { color: #aa5500 } /* Literal.String.Escape */ .highlight .sh { color: #aa5500 } /* Literal.String.Heredoc */ .highlight .si { color: #aa5500 } /* Literal.String.Interpol */ .highlight .sx { color: #aa5500 } /* Literal.String.Other */ .highlight .sr { color: #009999 } /* Literal.String.Regex */ .highlight .s1 { color: #aa5500 } /* Literal.String.Single */ .highlight .ss { color: #0000aa } /* Literal.String.Symbol */ .highlight .bp { color: #00aaaa } /* Name.Builtin.Pseudo */ .highlight .vc { color: #aa0000 } /* Name.Variable.Class */ .highlight .vg { color: #aa0000 } /* Name.Variable.Global */ .highlight .vi { color: #aa0000 } /* Name.Variable.Instance */ .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/borland.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #008800; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #000080; font-weight: bold } /* Keyword */ .highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #008080 } /* Comment.Preproc */ .highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */ .highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #999999 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #000080; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #000080; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #000080; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #000080; font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { color: #000080; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #000080; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000FF } /* Literal.Number */ .highlight .s { color: #0000FF } /* Literal.String */ .highlight .na { color: #FF0000 } /* Name.Attribute */ .highlight .nt { color: #000080; font-weight: bold } /* Name.Tag */ .highlight .ow { font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #0000FF } /* Literal.Number.Float */ .highlight .mh { color: #0000FF } /* Literal.Number.Hex */ .highlight .mi { color: #0000FF } /* Literal.Number.Integer */ .highlight .mo { color: #0000FF } /* Literal.Number.Oct */ .highlight .sb { color: #0000FF } /* Literal.String.Backtick */ .highlight .sc { color: #800080 } /* Literal.String.Char */ .highlight .sd { color: #0000FF } /* Literal.String.Doc */ .highlight .s2 { color: #0000FF } /* Literal.String.Double */ .highlight .se { color: #0000FF } /* Literal.String.Escape */ .highlight .sh { color: #0000FF } /* Literal.String.Heredoc */ .highlight .si { color: #0000FF } /* Literal.String.Interpol */ .highlight .sx { color: #0000FF } /* Literal.String.Other */ .highlight .sr { color: #0000FF } /* Literal.String.Regex */ .highlight .s1 { color: #0000FF } /* Literal.String.Single */ .highlight .ss { color: #0000FF } /* Literal.String.Symbol */ .highlight .il { color: #0000FF } /* Literal.Number.Integer.Long */ ================================================ FILE: css/bw.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { font-weight: bold } /* Keyword */ .highlight .cm { font-style: italic } /* Comment.Multiline */ .highlight .c1 { font-style: italic } /* Comment.Single */ .highlight .cs { font-style: italic } /* Comment.Special */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gh { font-weight: bold } /* Generic.Heading */ .highlight .gp { font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { font-weight: bold } /* Generic.Subheading */ .highlight .kc { font-weight: bold } /* Keyword.Constant */ .highlight .kd { font-weight: bold } /* Keyword.Declaration */ .highlight .kn { font-weight: bold } /* Keyword.Namespace */ .highlight .kr { font-weight: bold } /* Keyword.Reserved */ .highlight .s { font-style: italic } /* Literal.String */ .highlight .nc { font-weight: bold } /* Name.Class */ .highlight .ni { font-weight: bold } /* Name.Entity */ .highlight .ne { font-weight: bold } /* Name.Exception */ .highlight .nn { font-weight: bold } /* Name.Namespace */ .highlight .nt { font-weight: bold } /* Name.Tag */ .highlight .ow { font-weight: bold } /* Operator.Word */ .highlight .sb { font-style: italic } /* Literal.String.Backtick */ .highlight .sc { font-style: italic } /* Literal.String.Char */ .highlight .sd { font-style: italic } /* Literal.String.Doc */ .highlight .s2 { font-style: italic } /* Literal.String.Double */ .highlight .se { font-weight: bold; font-style: italic } /* Literal.String.Escape */ .highlight .sh { font-style: italic } /* Literal.String.Heredoc */ .highlight .si { font-weight: bold; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { font-style: italic } /* Literal.String.Other */ .highlight .sr { font-style: italic } /* Literal.String.Regex */ .highlight .s1 { font-style: italic } /* Literal.String.Single */ .highlight .ss { font-style: italic } /* Literal.String.Symbol */ ================================================ FILE: css/colorful.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #808080 } /* Comment */ .highlight .err { color: #F00000; background-color: #F0A0A0 } /* Error */ .highlight .k { color: #008000; font-weight: bold } /* Keyword */ .highlight .o { color: #303030 } /* Operator */ .highlight .cm { color: #808080 } /* Comment.Multiline */ .highlight .cp { color: #507090 } /* Comment.Preproc */ .highlight .c1 { color: #808080 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #303090; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #6000E0; font-weight: bold } /* Literal.Number */ .highlight .s { background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #0000C0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #B00060; font-weight: bold } /* Name.Class */ .highlight .no { color: #003060; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #505050; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #800000; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #F00000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0060B0; font-weight: bold } /* Name.Function */ .highlight .nl { color: #907000; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #007000 } /* Name.Tag */ .highlight .nv { color: #906030 } /* Name.Variable */ .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */ .highlight .sb { background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #0040D0 } /* Literal.String.Char */ .highlight .sd { color: #D04020 } /* Literal.String.Doc */ .highlight .s2 { background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { background-color: #e0e0e0 } /* Literal.String.Interpol */ .highlight .sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */ .highlight .sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #A06000 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #306090 } /* Name.Variable.Class */ .highlight .vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */ .highlight .vi { color: #3030B0 } /* Name.Variable.Instance */ .highlight .il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */ ================================================ FILE: css/default.css ================================================ /* Minimal reset */ * { vertical-align: baseline; font-family: inherit; font-style: inherit; font-size: 100%; border: none; padding: 0; margin: 0; } em { font-style: italic; } code { font-family: courier; } article, header, section, footer { display: block; } /* Colors */ a, .header a:visited { color: #26ADE4; text-decoration: none; } a:hover { text-decoration: underline; text-shadow: 0 0 0 #DCEEF5; } footer { border-top: 1px solid #eee; padding-top: 22px; margin: 21px 0 22px 0; color: #888; font-size: 14px; } /* Layout (margins, padding) */ pre { padding: 22px; line-height: 22px; margin: 22px 0; } p, header, section { margin-bottom: 22px; } ul.unstyled { list-style: none; } ul, ol { margin-left: 22px; margin-bottom: 22px; } nav ul { list-style:none; } nav ul li { border-left:1px solid rgba(0, 0, 0, 0.2); display:inline-block; padding:0 10px; } nav ul li:first-child { border:none; } nav ol { list-style:none; } nav ol li { display:inline-block; } nav ol.breadcrumbs li:before { content: '\000bb\000a0'; /* Insert »  between list items. */ } nav ol.breadcrumbs li:first-child:before { content: ''; } /* Typography */ body { text-rendering: optimizeLegibility; color: #373837; font-family: Palatino, "Palatino Linotype", serif; text-shadow: 0 0 0 transparent; font-size: 16px; line-height: 22px; margin: 22px; } pre { font-family: Monaco, Monospace; font-size: 14px; } h1, h2, h3 { margin: 0; padding: 0; } h1 { font-size: 30px; line-height: 44px; } h2 { font-size: 22px; line-height: 44px; } h3 { font-size: 18px; line-height: 22px; } /* Syntax Highlighting */ .highlight .hll { background-color: #ffffcc } .highlight { background: #f8f8f8; } .highlight .c { color: #408080; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #008000; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008000 } /* Keyword.Pseudo */ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #B00040 } /* Keyword.Type */ .highlight .m { color: #666666 } /* Literal.Number */ .highlight .s { color: #BA2121 } /* Literal.String */ .highlight .na { color: #7D9029 } /* Name.Attribute */ .highlight .nb { color: #008000 } /* Name.Builtin */ .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ .highlight .no { color: #880000 } /* Name.Constant */ .highlight .nd { color: #AA22FF } /* Name.Decorator */ .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0000FF } /* Name.Function */ .highlight .nl { color: #A0A000 } /* Name.Label */ .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #19177C } /* Name.Variable */ .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #666666 } /* Literal.Number.Float */ .highlight .mh { color: #666666 } /* Literal.Number.Hex */ .highlight .mi { color: #666666 } /* Literal.Number.Integer */ .highlight .mo { color: #666666 } /* Literal.Number.Oct */ .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ .highlight .sc { color: #BA2121 } /* Literal.String.Char */ .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ .highlight .sx { color: #008000 } /* Literal.String.Other */ .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ .highlight .ss { color: #19177C } /* Literal.String.Symbol */ .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #19177C } /* Name.Variable.Class */ .highlight .vg { color: #19177C } /* Name.Variable.Global */ .highlight .vi { color: #19177C } /* Name.Variable.Instance */ .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/emacs.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #008800; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #AA22FF; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #008800; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #008800 } /* Comment.Preproc */ .highlight .c1 { color: #008800; font-style: italic } /* Comment.Single */ .highlight .cs { color: #008800; font-weight: bold } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #AA22FF; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #AA22FF } /* Keyword.Pseudo */ .highlight .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #00BB00; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #666666 } /* Literal.Number */ .highlight .s { color: #BB4444 } /* Literal.String */ .highlight .na { color: #BB4444 } /* Name.Attribute */ .highlight .nb { color: #AA22FF } /* Name.Builtin */ .highlight .nc { color: #0000FF } /* Name.Class */ .highlight .no { color: #880000 } /* Name.Constant */ .highlight .nd { color: #AA22FF } /* Name.Decorator */ .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #00A000 } /* Name.Function */ .highlight .nl { color: #A0A000 } /* Name.Label */ .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #B8860B } /* Name.Variable */ .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #666666 } /* Literal.Number.Float */ .highlight .mh { color: #666666 } /* Literal.Number.Hex */ .highlight .mi { color: #666666 } /* Literal.Number.Integer */ .highlight .mo { color: #666666 } /* Literal.Number.Oct */ .highlight .sb { color: #BB4444 } /* Literal.String.Backtick */ .highlight .sc { color: #BB4444 } /* Literal.String.Char */ .highlight .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #BB4444 } /* Literal.String.Double */ .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #BB4444 } /* Literal.String.Heredoc */ .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ .highlight .sx { color: #008000 } /* Literal.String.Other */ .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ .highlight .s1 { color: #BB4444 } /* Literal.String.Single */ .highlight .ss { color: #B8860B } /* Literal.String.Symbol */ .highlight .bp { color: #AA22FF } /* Name.Builtin.Pseudo */ .highlight .vc { color: #B8860B } /* Name.Variable.Class */ .highlight .vg { color: #B8860B } /* Name.Variable.Global */ .highlight .vi { color: #B8860B } /* Name.Variable.Instance */ .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/friendly.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #60a0b0; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #007020; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #60a0b0; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #007020 } /* Comment.Preproc */ .highlight .c1 { color: #60a0b0; font-style: italic } /* Comment.Single */ .highlight .cs { color: #60a0b0; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #007020; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #007020; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #007020; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #007020 } /* Keyword.Pseudo */ .highlight .kr { color: #007020; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #902000 } /* Keyword.Type */ .highlight .m { color: #40a070 } /* Literal.Number */ .highlight .s { color: #4070a0 } /* Literal.String */ .highlight .na { color: #4070a0 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #0e84b5; font-weight: bold } /* Name.Class */ .highlight .no { color: #60add5 } /* Name.Constant */ .highlight .nd { color: #555555; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #d55537; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #007020 } /* Name.Exception */ .highlight .nf { color: #06287e } /* Name.Function */ .highlight .nl { color: #002070; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #062873; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #bb60d5 } /* Name.Variable */ .highlight .ow { color: #007020; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #40a070 } /* Literal.Number.Float */ .highlight .mh { color: #40a070 } /* Literal.Number.Hex */ .highlight .mi { color: #40a070 } /* Literal.Number.Integer */ .highlight .mo { color: #40a070 } /* Literal.Number.Oct */ .highlight .sb { color: #4070a0 } /* Literal.String.Backtick */ .highlight .sc { color: #4070a0 } /* Literal.String.Char */ .highlight .sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4070a0 } /* Literal.String.Double */ .highlight .se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #4070a0 } /* Literal.String.Heredoc */ .highlight .si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */ .highlight .sx { color: #c65d09 } /* Literal.String.Other */ .highlight .sr { color: #235388 } /* Literal.String.Regex */ .highlight .s1 { color: #4070a0 } /* Literal.String.Single */ .highlight .ss { color: #517918 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #bb60d5 } /* Name.Variable.Class */ .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/fruity.css ================================================ .highlight .hll { background-color: #333333 } .highlight .c { color: #008800; font-style: italic; background-color: #0f140f } /* Comment */ .highlight .err { color: #ffffff } /* Error */ .highlight .g { color: #ffffff } /* Generic */ .highlight .k { color: #fb660a; font-weight: bold } /* Keyword */ .highlight .l { color: #ffffff } /* Literal */ .highlight .n { color: #ffffff } /* Name */ .highlight .o { color: #ffffff } /* Operator */ .highlight .x { color: #ffffff } /* Other */ .highlight .p { color: #ffffff } /* Punctuation */ .highlight .cm { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Multiline */ .highlight .cp { color: #ff0007; font-weight: bold; font-style: italic; background-color: #0f140f } /* Comment.Preproc */ .highlight .c1 { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Single */ .highlight .cs { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Special */ .highlight .gd { color: #ffffff } /* Generic.Deleted */ .highlight .ge { color: #ffffff } /* Generic.Emph */ .highlight .gr { color: #ffffff } /* Generic.Error */ .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #ffffff } /* Generic.Inserted */ .highlight .go { color: #444444; background-color: #222222 } /* Generic.Output */ .highlight .gp { color: #ffffff } /* Generic.Prompt */ .highlight .gs { color: #ffffff } /* Generic.Strong */ .highlight .gu { color: #ffffff; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #ffffff } /* Generic.Traceback */ .highlight .kc { color: #fb660a; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #fb660a; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #fb660a; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #fb660a } /* Keyword.Pseudo */ .highlight .kr { color: #fb660a; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #cdcaa9; font-weight: bold } /* Keyword.Type */ .highlight .ld { color: #ffffff } /* Literal.Date */ .highlight .m { color: #0086f7; font-weight: bold } /* Literal.Number */ .highlight .s { color: #0086d2 } /* Literal.String */ .highlight .na { color: #ff0086; font-weight: bold } /* Name.Attribute */ .highlight .nb { color: #ffffff } /* Name.Builtin */ .highlight .nc { color: #ffffff } /* Name.Class */ .highlight .no { color: #0086d2 } /* Name.Constant */ .highlight .nd { color: #ffffff } /* Name.Decorator */ .highlight .ni { color: #ffffff } /* Name.Entity */ .highlight .ne { color: #ffffff } /* Name.Exception */ .highlight .nf { color: #ff0086; font-weight: bold } /* Name.Function */ .highlight .nl { color: #ffffff } /* Name.Label */ .highlight .nn { color: #ffffff } /* Name.Namespace */ .highlight .nx { color: #ffffff } /* Name.Other */ .highlight .py { color: #ffffff } /* Name.Property */ .highlight .nt { color: #fb660a; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #fb660a } /* Name.Variable */ .highlight .ow { color: #ffffff } /* Operator.Word */ .highlight .w { color: #888888 } /* Text.Whitespace */ .highlight .mf { color: #0086f7; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0086f7; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0086f7; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0086f7; font-weight: bold } /* Literal.Number.Oct */ .highlight .sb { color: #0086d2 } /* Literal.String.Backtick */ .highlight .sc { color: #0086d2 } /* Literal.String.Char */ .highlight .sd { color: #0086d2 } /* Literal.String.Doc */ .highlight .s2 { color: #0086d2 } /* Literal.String.Double */ .highlight .se { color: #0086d2 } /* Literal.String.Escape */ .highlight .sh { color: #0086d2 } /* Literal.String.Heredoc */ .highlight .si { color: #0086d2 } /* Literal.String.Interpol */ .highlight .sx { color: #0086d2 } /* Literal.String.Other */ .highlight .sr { color: #0086d2 } /* Literal.String.Regex */ .highlight .s1 { color: #0086d2 } /* Literal.String.Single */ .highlight .ss { color: #0086d2 } /* Literal.String.Symbol */ .highlight .bp { color: #ffffff } /* Name.Builtin.Pseudo */ .highlight .vc { color: #fb660a } /* Name.Variable.Class */ .highlight .vg { color: #fb660a } /* Name.Variable.Global */ .highlight .vi { color: #fb660a } /* Name.Variable.Instance */ .highlight .il { color: #0086f7; font-weight: bold } /* Literal.Number.Integer.Long */ ================================================ FILE: css/manni.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #0099FF; font-style: italic } /* Comment */ .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ .highlight .k { color: #006699; font-weight: bold } /* Keyword */ .highlight .o { color: #555555 } /* Operator */ .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #009999 } /* Comment.Preproc */ .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ .highlight .go { color: #AAAAAA } /* Generic.Output */ .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #99CC66 } /* Generic.Traceback */ .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #006699 } /* Keyword.Pseudo */ .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #FF6600 } /* Literal.Number */ .highlight .s { color: #CC3300 } /* Literal.String */ .highlight .na { color: #330099 } /* Name.Attribute */ .highlight .nb { color: #336666 } /* Name.Builtin */ .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ .highlight .no { color: #336600 } /* Name.Constant */ .highlight .nd { color: #9999FF } /* Name.Decorator */ .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #CC00FF } /* Name.Function */ .highlight .nl { color: #9999FF } /* Name.Label */ .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #003333 } /* Name.Variable */ .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #FF6600 } /* Literal.Number.Float */ .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ .highlight .sc { color: #CC3300 } /* Literal.String.Char */ .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #CC3300 } /* Literal.String.Double */ .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ .highlight .si { color: #AA0000 } /* Literal.String.Interpol */ .highlight .sx { color: #CC3300 } /* Literal.String.Other */ .highlight .sr { color: #33AAAA } /* Literal.String.Regex */ .highlight .s1 { color: #CC3300 } /* Literal.String.Single */ .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #003333 } /* Name.Variable.Class */ .highlight .vg { color: #003333 } /* Name.Variable.Global */ .highlight .vi { color: #003333 } /* Name.Variable.Instance */ .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/murphy.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #606060; font-style: italic } /* Comment */ .highlight .err { color: #F00000; background-color: #F0A0A0 } /* Error */ .highlight .k { color: #208090; font-weight: bold } /* Keyword */ .highlight .o { color: #303030 } /* Operator */ .highlight .cm { color: #606060; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #507090 } /* Comment.Preproc */ .highlight .c1 { color: #606060; font-style: italic } /* Comment.Single */ .highlight .cs { color: #c00000; font-weight: bold; font-style: italic } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #208090; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #208090; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #208090; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #0080f0; font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { color: #208090; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #6060f0; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #6000E0; font-weight: bold } /* Literal.Number */ .highlight .s { background-color: #e0e0ff } /* Literal.String */ .highlight .na { color: #000070 } /* Name.Attribute */ .highlight .nb { color: #007020 } /* Name.Builtin */ .highlight .nc { color: #e090e0; font-weight: bold } /* Name.Class */ .highlight .no { color: #50e0d0; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #505050; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #800000 } /* Name.Entity */ .highlight .ne { color: #F00000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #50e0d0; font-weight: bold } /* Name.Function */ .highlight .nl { color: #907000; font-weight: bold } /* Name.Label */ .highlight .nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #007000 } /* Name.Tag */ .highlight .nv { color: #003060 } /* Name.Variable */ .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #6060f0; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */ .highlight .sb { background-color: #e0e0ff } /* Literal.String.Backtick */ .highlight .sc { color: #8080F0 } /* Literal.String.Char */ .highlight .sd { color: #D04020 } /* Literal.String.Doc */ .highlight .s2 { background-color: #e0e0ff } /* Literal.String.Double */ .highlight .se { color: #606060; font-weight: bold; background-color: #e0e0ff } /* Literal.String.Escape */ .highlight .sh { background-color: #e0e0ff } /* Literal.String.Heredoc */ .highlight .si { background-color: #e0e0e0 } /* Literal.String.Interpol */ .highlight .sx { color: #f08080; background-color: #e0e0ff } /* Literal.String.Other */ .highlight .sr { color: #000000; background-color: #e0e0ff } /* Literal.String.Regex */ .highlight .s1 { background-color: #e0e0ff } /* Literal.String.Single */ .highlight .ss { color: #f0c080 } /* Literal.String.Symbol */ .highlight .bp { color: #007020 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #c0c0f0 } /* Name.Variable.Class */ .highlight .vg { color: #f08040 } /* Name.Variable.Global */ .highlight .vi { color: #a0a0f0 } /* Name.Variable.Instance */ .highlight .il { color: #6060f0; font-weight: bold } /* Literal.Number.Integer.Long */ ================================================ FILE: css/native.css ================================================ .highlight .hll { background-color: #404040 } .highlight .c { color: #999999; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .g { color: #d0d0d0 } /* Generic */ .highlight .k { color: #6ab825; font-weight: bold } /* Keyword */ .highlight .l { color: #d0d0d0 } /* Literal */ .highlight .n { color: #d0d0d0 } /* Name */ .highlight .o { color: #d0d0d0 } /* Operator */ .highlight .x { color: #d0d0d0 } /* Other */ .highlight .p { color: #d0d0d0 } /* Punctuation */ .highlight .cm { color: #999999; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #cd2828; font-weight: bold } /* Comment.Preproc */ .highlight .c1 { color: #999999; font-style: italic } /* Comment.Single */ .highlight .cs { color: #e50808; font-weight: bold; background-color: #520000 } /* Comment.Special */ .highlight .gd { color: #d22323 } /* Generic.Deleted */ .highlight .ge { color: #d0d0d0; font-style: italic } /* Generic.Emph */ .highlight .gr { color: #d22323 } /* Generic.Error */ .highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #589819 } /* Generic.Inserted */ .highlight .go { color: #cccccc } /* Generic.Output */ .highlight .gp { color: #aaaaaa } /* Generic.Prompt */ .highlight .gs { color: #d0d0d0; font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #ffffff; text-decoration: underline } /* Generic.Subheading */ .highlight .gt { color: #d22323 } /* Generic.Traceback */ .highlight .kc { color: #6ab825; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #6ab825; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #6ab825; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #6ab825 } /* Keyword.Pseudo */ .highlight .kr { color: #6ab825; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #6ab825; font-weight: bold } /* Keyword.Type */ .highlight .ld { color: #d0d0d0 } /* Literal.Date */ .highlight .m { color: #3677a9 } /* Literal.Number */ .highlight .s { color: #ed9d13 } /* Literal.String */ .highlight .na { color: #bbbbbb } /* Name.Attribute */ .highlight .nb { color: #24909d } /* Name.Builtin */ .highlight .nc { color: #447fcf; text-decoration: underline } /* Name.Class */ .highlight .no { color: #40ffff } /* Name.Constant */ .highlight .nd { color: #ffa500 } /* Name.Decorator */ .highlight .ni { color: #d0d0d0 } /* Name.Entity */ .highlight .ne { color: #bbbbbb } /* Name.Exception */ .highlight .nf { color: #447fcf } /* Name.Function */ .highlight .nl { color: #d0d0d0 } /* Name.Label */ .highlight .nn { color: #447fcf; text-decoration: underline } /* Name.Namespace */ .highlight .nx { color: #d0d0d0 } /* Name.Other */ .highlight .py { color: #d0d0d0 } /* Name.Property */ .highlight .nt { color: #6ab825; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #40ffff } /* Name.Variable */ .highlight .ow { color: #6ab825; font-weight: bold } /* Operator.Word */ .highlight .w { color: #666666 } /* Text.Whitespace */ .highlight .mf { color: #3677a9 } /* Literal.Number.Float */ .highlight .mh { color: #3677a9 } /* Literal.Number.Hex */ .highlight .mi { color: #3677a9 } /* Literal.Number.Integer */ .highlight .mo { color: #3677a9 } /* Literal.Number.Oct */ .highlight .sb { color: #ed9d13 } /* Literal.String.Backtick */ .highlight .sc { color: #ed9d13 } /* Literal.String.Char */ .highlight .sd { color: #ed9d13 } /* Literal.String.Doc */ .highlight .s2 { color: #ed9d13 } /* Literal.String.Double */ .highlight .se { color: #ed9d13 } /* Literal.String.Escape */ .highlight .sh { color: #ed9d13 } /* Literal.String.Heredoc */ .highlight .si { color: #ed9d13 } /* Literal.String.Interpol */ .highlight .sx { color: #ffa500 } /* Literal.String.Other */ .highlight .sr { color: #ed9d13 } /* Literal.String.Regex */ .highlight .s1 { color: #ed9d13 } /* Literal.String.Single */ .highlight .ss { color: #ed9d13 } /* Literal.String.Symbol */ .highlight .bp { color: #24909d } /* Name.Builtin.Pseudo */ .highlight .vc { color: #40ffff } /* Name.Variable.Class */ .highlight .vg { color: #40ffff } /* Name.Variable.Global */ .highlight .vi { color: #40ffff } /* Name.Variable.Instance */ .highlight .il { color: #3677a9 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/pastie.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #303030 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #606060 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ ================================================ FILE: css/perldoc.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #228B22 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #8B008B; font-weight: bold } /* Keyword */ .highlight .cm { color: #228B22 } /* Comment.Multiline */ .highlight .cp { color: #1e889b } /* Comment.Preproc */ .highlight .c1 { color: #228B22 } /* Comment.Single */ .highlight .cs { color: #8B008B; font-weight: bold } /* Comment.Special */ .highlight .gd { color: #aa0000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00aa00 } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #8B008B; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #8B008B; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #8B008B; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #8B008B; font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { color: #8B008B; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #a7a7a7; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #B452CD } /* Literal.Number */ .highlight .s { color: #CD5555 } /* Literal.String */ .highlight .na { color: #658b00 } /* Name.Attribute */ .highlight .nb { color: #658b00 } /* Name.Builtin */ .highlight .nc { color: #008b45; font-weight: bold } /* Name.Class */ .highlight .no { color: #00688B } /* Name.Constant */ .highlight .nd { color: #707a7c } /* Name.Decorator */ .highlight .ne { color: #008b45; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #008b45 } /* Name.Function */ .highlight .nn { color: #008b45; text-decoration: underline } /* Name.Namespace */ .highlight .nt { color: #8B008B; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #00688B } /* Name.Variable */ .highlight .ow { color: #8B008B } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #B452CD } /* Literal.Number.Float */ .highlight .mh { color: #B452CD } /* Literal.Number.Hex */ .highlight .mi { color: #B452CD } /* Literal.Number.Integer */ .highlight .mo { color: #B452CD } /* Literal.Number.Oct */ .highlight .sb { color: #CD5555 } /* Literal.String.Backtick */ .highlight .sc { color: #CD5555 } /* Literal.String.Char */ .highlight .sd { color: #CD5555 } /* Literal.String.Doc */ .highlight .s2 { color: #CD5555 } /* Literal.String.Double */ .highlight .se { color: #CD5555 } /* Literal.String.Escape */ .highlight .sh { color: #1c7e71; font-style: italic } /* Literal.String.Heredoc */ .highlight .si { color: #CD5555 } /* Literal.String.Interpol */ .highlight .sx { color: #cb6c20 } /* Literal.String.Other */ .highlight .sr { color: #1c7e71 } /* Literal.String.Regex */ .highlight .s1 { color: #CD5555 } /* Literal.String.Single */ .highlight .ss { color: #CD5555 } /* Literal.String.Symbol */ .highlight .bp { color: #658b00 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #00688B } /* Name.Variable.Class */ .highlight .vg { color: #00688B } /* Name.Variable.Global */ .highlight .vi { color: #00688B } /* Name.Variable.Instance */ .highlight .il { color: #B452CD } /* Literal.Number.Integer.Long */ ================================================ FILE: css/solarized-light.css ================================================ /* base03: #002b36; base02: #073642; base01: #586e75; base00: #657b83; base0: #839496; base1: #93a1a1; base2: #eee8d5; base3: #fdf6e3; yellow: #b58900; orange: #cb4b16; red: #dc322f; magenta: #d33682; violet: #6c71c4; blue: #268bd2; cyan: #2aa198; green: #859900; */ .highlight { background: #fdf6e3; color: #657b83; line-height: 1em; font-size: 13px; } .highlight .lineno { background-color: #eee8d5; color: #93a1a1; padding-left: .5em; padding-right: .5em; } .highlight .hll { background-color: #eee8d5; } /* Don't know what this is... */ .highlight .c { color: #93a1a1; font-style: italic; } /* Comment */ .highlight .err { color: #dc322f; } /* Error */ .highlight .g { color: #839496; } /* Generic */ .highlight .k { color: #859900; } /* Keyword */ .highlight .l { color: #839496; } /* Literal */ .highlight .n { color: #839496; } /* Name */ .highlight .o { color: #859900; } /* Operator */ .highlight .x { color: #839496; } /* Other */ .highlight .p { color: #839496; } /* Punctuation */ .highlight .cm { color: #93a1a1; font-style: italic; } /* Comment.Multiline */ .highlight .cp { color: #93a1a1; font-style: italic; } /* Comment.Preproc */ .highlight .c1 { color: #93a1a1; font-style: italic; } /* Comment.Single */ .highlight .cs { color: #93a1a1; font-style: italic; } /* Comment.Special */ .highlight .gd { color: #dc322f; } /* Generic.Deleted */ .highlight .ge { color: #839496; font-style: italic; } /* Generic.Emph */ .highlight .gr { color: #839496; } /* Generic.Error */ .highlight .gh { color: #839496; font-weight: bold; } /* Generic.Heading */ .highlight .gi { color: #859900; } /* Generic.Inserted */ .highlight .go { color: #839496; } /* Generic.Output */ .highlight .gp { color: #839496; font-weight: bold; } /* Generic.Prompt */ .highlight .gs { color: #839496; font-weight: bold; } /* Generic.Strong */ .highlight .gu { color: #839496; font-weight: bold; } /* Generic.Subheading */ .highlight .gt { color: #839496; } /* Generic.Traceback */ .highlight .kc { color: #839496; } /* Keyword.Constant */ .highlight .kd { color: #839496; } /* Keyword.Declaration */ .highlight .kn { color: #839496; } /* Keyword.Namespace */ .highlight .kp { color: #2aa198; } /* Keyword.Pseudo */ .highlight .kr { color: #839496; } /* Keyword.Reserved */ .highlight .kt { color: #839496; } /* Keyword.Type */ .highlight .ld { color: #839496; } /* Literal.Date */ .highlight .m { color: #839496; } /* Literal.Number */ .highlight .s { color: #2aa198; } /* Literal.String */ .highlight .na { color: #839496; } /* Name.Attribute */ .highlight .nb { color: #cb4b16; } /* Name.Builtin */ .highlight .nc { color: #839496; } /* Name.Class */ .highlight .no { color: #b58900; } /* Name.Constant */ .highlight .nd { color: #839496; } /* Name.Decorator */ .highlight .ni { color: #839496; } /* Name.Entity */ .highlight .ne { color: #839496; } /* Name.Exception */ .highlight .nf { color: #268bd2; } /* Name.Function */ .highlight .nl { color: #839496; } /* Name.Label */ .highlight .nn { color: #cb4b16; } /* Name.Namespace */ .highlight .nx { color: #839496; } /* Name.Other */ .highlight .py { color: #839496; } /* Name.Property */ .highlight .nt { color: #839496; } /* Name.Tag */ .highlight .nv { color: #839496; } /* Name.Variable */ .highlight .ow { color: #839496; } /* Operator.Word */ .highlight .w { color: #839496; } /* Text.Whitespace */ .highlight .mf { color: #2aa198; } /* Literal.Number.Float */ .highlight .mh { color: #2aa198; } /* Literal.Number.Hex */ .highlight .mi { color: #2aa198; } /* Literal.Number.Integer */ .highlight .mo { color: #2aa198; } /* Literal.Number.Oct */ .highlight .sb { color: #2aa198; } /* Literal.String.Backtick */ .highlight .sc { color: #2aa198; } /* Literal.String.Char */ .highlight .sd { color: #2aa198; } /* Literal.String.Doc */ .highlight .s2 { color: #2aa198; } /* Literal.String.Double */ .highlight .se { color: #dc322f; } /* Literal.String.Escape */ .highlight .sh { color: #2aa198; } /* Literal.String.Heredoc */ .highlight .si { color: #2aa198; } /* Literal.String.Interpol */ .highlight .sx { color: #2aa198; } /* Literal.String.Other */ .highlight .sr { color: #2aa198; } /* Literal.String.Regex */ .highlight .s1 { color: #2aa198; } /* Literal.String.Single */ .highlight .ss { color: #2aa198; } /* Literal.String.Symbol */ .highlight .bp { color: #839496; } /* Name.Builtin.Pseudo */ .highlight .vc { color: #839496; } /* Name.Variable.Class */ .highlight .vg { color: #839496; } /* Name.Variable.Global */ .highlight .vi { color: #839496; } /* Name.Variable.Instance */ .highlight .il { color: #839496; } /* Literal.Number.Integer.Long */ ================================================ FILE: css/style.css ================================================ /* Author: Amsul - http://github.com/amsul Description: CoffeeScript Cookbook stylings Version: 1.0 Last Updated: 11 September, 2012 */ /* Font-face ======================================================================== */ @font-face { font-family: 'Entypo'; src: url(data:application/x-font-woff;charset=utf-8;base64,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) format('woff'), url(../fonts/entypo.ttf) format('truetype'), url(../fonts/entypo.svg#EntypoRegular) format('svg'); } /* ======================================================================== Cross-browser normalize (from HTML5 Boilerplate v4.0) ======================================================================== */ /* HTML5 element display (old browsers) ======================================================================== */ article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary { display: block; } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1; } audio:not([controls]) { display: none; height: 0; } [hidden] { display: none; } /* Base ======================================================================== */ html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } html, button, input, select, textarea { font-family: "Lucida Grande", "Lucida Sans Unicode", sans-serif; font-weight: 200; color: #424242; } body { margin: 0; } ::-moz-selection { background: #b3d4fc; text-shadow: none; } ::selection { background: #b3d4fc; text-shadow: none; } /* Links ======================================================================== */ a:hover, a:active { outline: 0; } a:focus { outline: thin dotted; } /* Typography ======================================================================== */ h1 { font-size: 2em; margin: .67em 0; } h2 { font-size: 1.5em; margin: .83em 0; } h3 { font-size: 1.17em; margin: 1em 0; } h4 { font-size: 1em; margin: 1.33em 0; } h5 { font-size: .83em; margin: 1.67em 0; } h6 { font-size: .75em; margin: 2.33em 0; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } blockquote { margin: 1em 40px; } dfn { font-style: italic; } mark { background: #ff0; color: #000; } p, pre { margin: 1em 0; } pre, code, kbd, samp { font-family: 'Courier', 'Monaco', monospace, serif; font-size: 1em; } pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; } q { quotes: none; } q:before, q:after { content: ""; content: none; } small { font-size: 80%; } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } sup { top: -.5em; } sub { bottom: -.25em; } /* Lists ======================================================================== */ dl, menu, ol, ul { margin: 1em 0; } dd { margin: 0 0 0 40px; } menu, ol, ul { padding: 0 0 0 40px; } nav ul, nav ol { list-style: none; list-style-image: none; } /* Embedded content ======================================================================== */ object, embed, img { max-width: 100%; } img { border: 0; -ms-interpolation-mode: bicubic; } svg:not(:root) { overflow: hidden; } /* Figures ======================================================================== */ figure { margin: 0; } /* Forms ======================================================================== */ form { margin: 0; } fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: .35em .625em .75em; } legend { border: 0; padding: 0; white-space: normal; } label:hover { cursor: default; } button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; } button, html input[type=button], input[type=reset], input[type=submit] { -webkit-appearance: button; cursor: pointer; } button[disabled], input[disabled] { cursor: default; } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0; } input[type=checkbox], input[type=radio] { cursor: pointer; padding: 0; } input[type=search] { -webkit-appearance: textfield; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; } textarea { overflow: auto; vertical-align: top; resize: vertical; } /* Tables ======================================================================== */ table { border-spacing: 0; border-collapse: collapse; } /* ======================================================================== ======================================================================== ======================================================================== */ /* ======================================================================== Primary styling ======================================================================== */ /* Base ======================================================================== */ body { font-size: 16px; line-height: 1.5; } hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; } img { vertical-align: middle; height: auto; max-width: 100%; } a { cursor: pointer; text-decoration: underline; color: #27ace3; } h1, h2, h3, h4, h5, h6 { font-weight: bold; line-height: 1.2; } h1:first-child, h2:first-child, h3:first-child, h4:first-child, h5:first-child, h6:first-child, p:first-child { margin-top: 0; } h1 { font-size: 32px; margin-bottom: .66em; } h2 { font-size: 24px; margin: 1.33em 0 .66em; } h3 { font-size: 20px; margin-bottom: .25em; } h4 { font-size: 18px; margin-bottom: .75em; } h5 { font-size: 16px; margin: 1.5em 0 .75em; } h6 { font-size: 14px; margin: 1.5em 0 .25em; } /* Lists ======================================================================== */ ul:not(.plain-list) li { margin-bottom: 5px; } ul.plain-list, .pseudo-list { list-style: none; margin: 0; padding: 0; } .pseudo-list li { position: relative; padding-left: 14px; } .pseudo-list li:before { content: "\2013"; position: absolute; margin-left: -12px; } .inline-list li { display: inline-block; margin-right: 20px; } /* Tables ======================================================================== */ table { background: #fff; border: 1px solid #ccc; line-height: 1.5; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } th, td { vertical-align: top; padding: 6px 12px; border-top: 1px solid #e2e2e2; border-right: 1px solid #e2e2e2; } th:last-child, td:last-child { border-right: 0; } thead th { background: #f5f5f5; border-top: 0; } thead th:first-child { -webkit-border-radius: 5px 0 0 0; -moz-border-radius: 5px 0 0 0; border-radius: 5px 0 0 0; } thead th:last-child { -webkit-border-radius: 0 5px 0 0; -moz-border-radius: 0 5px 0 0; border-radius: 0 5px 0 0; } tbody th, tbody td { font-size: 12px; } tbody tr:nth-child(even) th, tbody tr:nth-child(even) td { background-color: #f9f9f9; } tbody tr:last-child th:first-child, tbody tr:last-child td:first-child { -webkit-border-radius: 0 0 0 5px; -moz-border-radius: 0 0 0 5px; border-radius: 0 0 0 5px; } tbody tr:last-child th:last-child, tbody tr:last-child td:last-child { -webkit-border-radius: 0 0 5px 0; -moz-border-radius: 0 0 5px 0; border-radius: 0 0 5px 0; } /* Form elements ======================================================================== */ fieldset { border: 0; margin: 0 0 14px; padding: 0; } input[type=checkbox], input[type=radio] { margin-right: 5px; } label:hover { color: #27ace3; } /* Code blocks ======================================================================== */ pre { display: table; width: 100%; margin: 0; padding: 24px; } pre > code { display: block; } /* Site structure ======================================================================== */ .container { margin-left: 444px; min-width: 666px; max-width: 1000px; padding: 40px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .header a, .navigation a { text-decoration: none; } .navigation:not(.pages-nav) a:hover { text-decoration: underline; } /** Header **/ .header { margin-bottom: 32px; } .header .entypo { font-size: 82px; line-height: 0.2; margin-right: 10px; color: #27ace3; position: relative; top: -2px; } .header .title { background: url(../images/title.png) no-repeat 0 0 transparent; width: 421px; height: 33px; display: inline-block; vertical-align: top; } /** Footer **/ .footer { border-top: 1px solid #eee; padding-top: 42px; font-size: 12px; color: #888; } /** Content blocks and sections **/ .content { margin-right: 250px; } .content-block { margin-bottom: 100px; } .content-full { width: auto; } /* Sidebar ======================================================================== */ #sidebar { background: #f0f5f6; border-right: 1px solid #e0e5e6; position: fixed; top: 0; left: 0; bottom: 0; width: 444px; padding: 40px 20px 40px 40px; font-size: 12px; overflow-y: scroll; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .sidebar-block { margin-bottom: 54px; } .sidebar-block .navigation a { color: #424242; } .block-short { margin-bottom: 24px; } .sidebar-subtitle { font-size: 14px; margin-bottom: 6px; } /** Pages nav **/ .pages-nav .nav-link { text-transform: uppercase; } .pages-nav .nav-link:hover { text-decoration: none; color: #27ace3; } .pages-nav .entypo { font-size: 36px; vertical-align: baseline; position: relative; top: 4px; margin-right: 8px; line-height: .5; } .home-nav .nav-link { text-transform: none; } .home-nav .entypo { margin-right: 4px; } /** Chapters index **/ .chapters-list { margin: 0; } .chapter-title { color: #424242 } .recipes-list { line-height: 1.25; margin-bottom: 24px; } .recipes-list .list-item { color: #888; } /* Recipes page ======================================================================== */ .recipe-content h1 { font-size: 28px; } .recipe-content h2 { font-size: 20px; margin-bottom: .5em; } .recipe-content p { margin-top: 0; } /* Active page ======================================================================== */ .page-active { position: relative; background: #fff; padding-right: 24px; -webkit-border-radius: 2px; -moz-border-radius: 2px; border-radius: 2px; } .page-active:after { content: 'M'; position: absolute; top: 4px; right: 0; font-family: 'Entypo'; font-size: 36px; line-height: 6px; color: #666; } .recipes-list .page-active:after { top: 1px; } /* ======================================================================== ======================================================================== ======================================================================== */ /* Non-semantic helper classes ======================================================================== */ .dimmed, a.dimmed { color: #888; } .warning { color: red; } .hidden { display: none; visibility: hidden; } .invisible { visibility: hidden; } .thin { font-weight: 100; } .entypo { font-family: 'Entypo'; line-height: 1; text-transform: initial; } .alignleft { float: left; } .alignright { float: right; } .clear:before, .clear:after { content: " "; display: table; } .clear:after { clear: both; } .clear { *zoom: 1; } .ir { background-color: transparent; border: 0; overflow: hidden; *text-indent: -9999px; } .ir:before { content: ""; display: block; width: 0; height: 100%; } /* Code highlighting ======================================================================== */ /* base03: #002b36; base02: #073642; base01: #586e75; base00: #657b83; base0: #839496; base1: #93a1a1; base2: #eee8d5; base3: #fdf6e3; yellow: #b58900; orange: #cb4b16; red: #dc322f; magenta: #d33682; violet: #6c71c4; blue: #268bd2; cyan: #2aa198; green: #859900; */ .highlight { background: #f8f8f8; font-size: 14px; overflow-x: scroll; margin-right: -250px; margin-bottom: 36px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #408080; font-style: italic } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #008000; font-weight: bold } /* Keyword */ .highlight .o { color: #666666 } /* Operator */ .highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #BC7A00 } /* Comment.Preproc */ .highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */ .highlight .cs { color: #408080; font-style: italic } /* Comment.Special */ .highlight .gd { color: #A00000 } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008000 } /* Keyword.Pseudo */ .highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #B00040 } /* Keyword.Type */ .highlight .m { color: #666666 } /* Literal.Number */ .highlight .s { color: #BA2121 } /* Literal.String */ .highlight .na { color: #7D9029 } /* Name.Attribute */ .highlight .nb { color: #008000 } /* Name.Builtin */ .highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */ .highlight .no { color: #880000 } /* Name.Constant */ .highlight .nd { color: #AA22FF } /* Name.Decorator */ .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ .highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0000FF } /* Name.Function */ .highlight .nl { color: #A0A000 } /* Name.Label */ .highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ .highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #19177C } /* Name.Variable */ .highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #666666 } /* Literal.Number.Float */ .highlight .mh { color: #666666 } /* Literal.Number.Hex */ .highlight .mi { color: #666666 } /* Literal.Number.Integer */ .highlight .mo { color: #666666 } /* Literal.Number.Oct */ .highlight .sb { color: #BA2121 } /* Literal.String.Backtick */ .highlight .sc { color: #BA2121 } /* Literal.String.Char */ .highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #BA2121 } /* Literal.String.Double */ .highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ .highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */ .highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ .highlight .sx { color: #008000 } /* Literal.String.Other */ .highlight .sr { color: #BB6688 } /* Literal.String.Regex */ .highlight .s1 { color: #BA2121 } /* Literal.String.Single */ .highlight .ss { color: #19177C } /* Literal.String.Symbol */ .highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #19177C } /* Name.Variable.Class */ .highlight .vg { color: #19177C } /* Name.Variable.Global */ .highlight .vi { color: #19177C } /* Name.Variable.Instance */ .highlight .il { color: #666666 } /* Literal.Number.Integer.Long */ /* ========================================================================== Media Queries ========================================================================== */ @media (max-width: 1110px) { body:after { content: 'narrow'; display: none; } #sidebar { position: absolute; bottom: auto; border-bottom: 1px solid #e0e5e6; } } /* Print media queries ======================================================================== */ @media print { * { background: transparent !important; color: #000 !important; box-shadow:none !important; text-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 0.5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } } ================================================ FILE: css/tango.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #8f5902; font-style: italic } /* Comment */ .highlight .err { color: #a40000; border: 1px solid #ef2929 } /* Error */ .highlight .g { color: #000000 } /* Generic */ .highlight .k { color: #204a87; font-weight: bold } /* Keyword */ .highlight .l { color: #000000 } /* Literal */ .highlight .n { color: #000000 } /* Name */ .highlight .o { color: #ce5c00; font-weight: bold } /* Operator */ .highlight .x { color: #000000 } /* Other */ .highlight .p { color: #000000; font-weight: bold } /* Punctuation */ .highlight .cm { color: #8f5902; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #8f5902; font-style: italic } /* Comment.Preproc */ .highlight .c1 { color: #8f5902; font-style: italic } /* Comment.Single */ .highlight .cs { color: #8f5902; font-style: italic } /* Comment.Special */ .highlight .gd { color: #a40000 } /* Generic.Deleted */ .highlight .ge { color: #000000; font-style: italic } /* Generic.Emph */ .highlight .gr { color: #ef2929 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00A000 } /* Generic.Inserted */ .highlight .go { color: #000000; font-style: italic } /* Generic.Output */ .highlight .gp { color: #8f5902 } /* Generic.Prompt */ .highlight .gs { color: #000000; font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #a40000; font-weight: bold } /* Generic.Traceback */ .highlight .kc { color: #204a87; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #204a87; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #204a87; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #204a87; font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { color: #204a87; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #204a87; font-weight: bold } /* Keyword.Type */ .highlight .ld { color: #000000 } /* Literal.Date */ .highlight .m { color: #0000cf; font-weight: bold } /* Literal.Number */ .highlight .s { color: #4e9a06 } /* Literal.String */ .highlight .na { color: #c4a000 } /* Name.Attribute */ .highlight .nb { color: #204a87 } /* Name.Builtin */ .highlight .nc { color: #000000 } /* Name.Class */ .highlight .no { color: #000000 } /* Name.Constant */ .highlight .nd { color: #5c35cc; font-weight: bold } /* Name.Decorator */ .highlight .ni { color: #ce5c00 } /* Name.Entity */ .highlight .ne { color: #cc0000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #000000 } /* Name.Function */ .highlight .nl { color: #f57900 } /* Name.Label */ .highlight .nn { color: #000000 } /* Name.Namespace */ .highlight .nx { color: #000000 } /* Name.Other */ .highlight .py { color: #000000 } /* Name.Property */ .highlight .nt { color: #204a87; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #000000 } /* Name.Variable */ .highlight .ow { color: #204a87; font-weight: bold } /* Operator.Word */ .highlight .w { color: #f8f8f8; text-decoration: underline } /* Text.Whitespace */ .highlight .mf { color: #0000cf; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000cf; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000cf; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000cf; font-weight: bold } /* Literal.Number.Oct */ .highlight .sb { color: #4e9a06 } /* Literal.String.Backtick */ .highlight .sc { color: #4e9a06 } /* Literal.String.Char */ .highlight .sd { color: #8f5902; font-style: italic } /* Literal.String.Doc */ .highlight .s2 { color: #4e9a06 } /* Literal.String.Double */ .highlight .se { color: #4e9a06 } /* Literal.String.Escape */ .highlight .sh { color: #4e9a06 } /* Literal.String.Heredoc */ .highlight .si { color: #4e9a06 } /* Literal.String.Interpol */ .highlight .sx { color: #4e9a06 } /* Literal.String.Other */ .highlight .sr { color: #4e9a06 } /* Literal.String.Regex */ .highlight .s1 { color: #4e9a06 } /* Literal.String.Single */ .highlight .ss { color: #4e9a06 } /* Literal.String.Symbol */ .highlight .bp { color: #3465a4 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #000000 } /* Name.Variable.Class */ .highlight .vg { color: #000000 } /* Name.Variable.Global */ .highlight .vi { color: #000000 } /* Name.Variable.Instance */ .highlight .il { color: #0000cf; font-weight: bold } /* Literal.Number.Integer.Long */ ================================================ FILE: css/trac.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #999988; font-style: italic } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { font-weight: bold } /* Keyword */ .highlight .o { font-weight: bold } /* Operator */ .highlight .cm { color: #999988; font-style: italic } /* Comment.Multiline */ .highlight .cp { color: #999999; font-weight: bold } /* Comment.Preproc */ .highlight .c1 { color: #999988; font-style: italic } /* Comment.Single */ .highlight .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #999999 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #aaaaaa } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { font-weight: bold } /* Keyword.Constant */ .highlight .kd { font-weight: bold } /* Keyword.Declaration */ .highlight .kn { font-weight: bold } /* Keyword.Namespace */ .highlight .kp { font-weight: bold } /* Keyword.Pseudo */ .highlight .kr { font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #009999 } /* Literal.Number */ .highlight .s { color: #bb8844 } /* Literal.String */ .highlight .na { color: #008080 } /* Name.Attribute */ .highlight .nb { color: #999999 } /* Name.Builtin */ .highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ .highlight .no { color: #008080 } /* Name.Constant */ .highlight .ni { color: #800080 } /* Name.Entity */ .highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ .highlight .nn { color: #555555 } /* Name.Namespace */ .highlight .nt { color: #000080 } /* Name.Tag */ .highlight .nv { color: #008080 } /* Name.Variable */ .highlight .ow { font-weight: bold } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mf { color: #009999 } /* Literal.Number.Float */ .highlight .mh { color: #009999 } /* Literal.Number.Hex */ .highlight .mi { color: #009999 } /* Literal.Number.Integer */ .highlight .mo { color: #009999 } /* Literal.Number.Oct */ .highlight .sb { color: #bb8844 } /* Literal.String.Backtick */ .highlight .sc { color: #bb8844 } /* Literal.String.Char */ .highlight .sd { color: #bb8844 } /* Literal.String.Doc */ .highlight .s2 { color: #bb8844 } /* Literal.String.Double */ .highlight .se { color: #bb8844 } /* Literal.String.Escape */ .highlight .sh { color: #bb8844 } /* Literal.String.Heredoc */ .highlight .si { color: #bb8844 } /* Literal.String.Interpol */ .highlight .sx { color: #bb8844 } /* Literal.String.Other */ .highlight .sr { color: #808000 } /* Literal.String.Regex */ .highlight .s1 { color: #bb8844 } /* Literal.String.Single */ .highlight .ss { color: #bb8844 } /* Literal.String.Symbol */ .highlight .bp { color: #999999 } /* Name.Builtin.Pseudo */ .highlight .vc { color: #008080 } /* Name.Variable.Class */ .highlight .vg { color: #008080 } /* Name.Variable.Global */ .highlight .vi { color: #008080 } /* Name.Variable.Instance */ .highlight .il { color: #009999 } /* Literal.Number.Integer.Long */ ================================================ FILE: css/vim.css ================================================ .highlight .hll { background-color: #222222 } .highlight .c { color: #000080 } /* Comment */ .highlight .err { color: #cccccc; border: 1px solid #FF0000 } /* Error */ .highlight .g { color: #cccccc } /* Generic */ .highlight .k { color: #cdcd00 } /* Keyword */ .highlight .l { color: #cccccc } /* Literal */ .highlight .n { color: #cccccc } /* Name */ .highlight .o { color: #3399cc } /* Operator */ .highlight .x { color: #cccccc } /* Other */ .highlight .p { color: #cccccc } /* Punctuation */ .highlight .cm { color: #000080 } /* Comment.Multiline */ .highlight .cp { color: #000080 } /* Comment.Preproc */ .highlight .c1 { color: #000080 } /* Comment.Single */ .highlight .cs { color: #cd0000; font-weight: bold } /* Comment.Special */ .highlight .gd { color: #cd0000 } /* Generic.Deleted */ .highlight .ge { color: #cccccc; font-style: italic } /* Generic.Emph */ .highlight .gr { color: #FF0000 } /* Generic.Error */ .highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */ .highlight .gi { color: #00cd00 } /* Generic.Inserted */ .highlight .go { color: #808080 } /* Generic.Output */ .highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ .highlight .gs { color: #cccccc; font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ .highlight .gt { color: #0040D0 } /* Generic.Traceback */ .highlight .kc { color: #cdcd00 } /* Keyword.Constant */ .highlight .kd { color: #00cd00 } /* Keyword.Declaration */ .highlight .kn { color: #cd00cd } /* Keyword.Namespace */ .highlight .kp { color: #cdcd00 } /* Keyword.Pseudo */ .highlight .kr { color: #cdcd00 } /* Keyword.Reserved */ .highlight .kt { color: #00cd00 } /* Keyword.Type */ .highlight .ld { color: #cccccc } /* Literal.Date */ .highlight .m { color: #cd00cd } /* Literal.Number */ .highlight .s { color: #cd0000 } /* Literal.String */ .highlight .na { color: #cccccc } /* Name.Attribute */ .highlight .nb { color: #cd00cd } /* Name.Builtin */ .highlight .nc { color: #00cdcd } /* Name.Class */ .highlight .no { color: #cccccc } /* Name.Constant */ .highlight .nd { color: #cccccc } /* Name.Decorator */ .highlight .ni { color: #cccccc } /* Name.Entity */ .highlight .ne { color: #666699; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #cccccc } /* Name.Function */ .highlight .nl { color: #cccccc } /* Name.Label */ .highlight .nn { color: #cccccc } /* Name.Namespace */ .highlight .nx { color: #cccccc } /* Name.Other */ .highlight .py { color: #cccccc } /* Name.Property */ .highlight .nt { color: #cccccc } /* Name.Tag */ .highlight .nv { color: #00cdcd } /* Name.Variable */ .highlight .ow { color: #cdcd00 } /* Operator.Word */ .highlight .w { color: #cccccc } /* Text.Whitespace */ .highlight .mf { color: #cd00cd } /* Literal.Number.Float */ .highlight .mh { color: #cd00cd } /* Literal.Number.Hex */ .highlight .mi { color: #cd00cd } /* Literal.Number.Integer */ .highlight .mo { color: #cd00cd } /* Literal.Number.Oct */ .highlight .sb { color: #cd0000 } /* Literal.String.Backtick */ .highlight .sc { color: #cd0000 } /* Literal.String.Char */ .highlight .sd { color: #cd0000 } /* Literal.String.Doc */ .highlight .s2 { color: #cd0000 } /* Literal.String.Double */ .highlight .se { color: #cd0000 } /* Literal.String.Escape */ .highlight .sh { color: #cd0000 } /* Literal.String.Heredoc */ .highlight .si { color: #cd0000 } /* Literal.String.Interpol */ .highlight .sx { color: #cd0000 } /* Literal.String.Other */ .highlight .sr { color: #cd0000 } /* Literal.String.Regex */ .highlight .s1 { color: #cd0000 } /* Literal.String.Single */ .highlight .ss { color: #cd0000 } /* Literal.String.Symbol */ .highlight .bp { color: #cd00cd } /* Name.Builtin.Pseudo */ .highlight .vc { color: #00cdcd } /* Name.Variable.Class */ .highlight .vg { color: #00cdcd } /* Name.Variable.Global */ .highlight .vi { color: #00cdcd } /* Name.Variable.Instance */ .highlight .il { color: #cd00cd } /* Literal.Number.Integer.Long */ ================================================ FILE: css/vs.css ================================================ .highlight .hll { background-color: #ffffcc } .highlight .c { color: #008000 } /* Comment */ .highlight .err { border: 1px solid #FF0000 } /* Error */ .highlight .k { color: #0000ff } /* Keyword */ .highlight .cm { color: #008000 } /* Comment.Multiline */ .highlight .cp { color: #0000ff } /* Comment.Preproc */ .highlight .c1 { color: #008000 } /* Comment.Single */ .highlight .cs { color: #008000 } /* Comment.Special */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gh { font-weight: bold } /* Generic.Heading */ .highlight .gp { font-weight: bold } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { font-weight: bold } /* Generic.Subheading */ .highlight .kc { color: #0000ff } /* Keyword.Constant */ .highlight .kd { color: #0000ff } /* Keyword.Declaration */ .highlight .kn { color: #0000ff } /* Keyword.Namespace */ .highlight .kp { color: #0000ff } /* Keyword.Pseudo */ .highlight .kr { color: #0000ff } /* Keyword.Reserved */ .highlight .kt { color: #2b91af } /* Keyword.Type */ .highlight .s { color: #a31515 } /* Literal.String */ .highlight .nc { color: #2b91af } /* Name.Class */ .highlight .ow { color: #0000ff } /* Operator.Word */ .highlight .sb { color: #a31515 } /* Literal.String.Backtick */ .highlight .sc { color: #a31515 } /* Literal.String.Char */ .highlight .sd { color: #a31515 } /* Literal.String.Doc */ .highlight .s2 { color: #a31515 } /* Literal.String.Double */ .highlight .se { color: #a31515 } /* Literal.String.Escape */ .highlight .sh { color: #a31515 } /* Literal.String.Heredoc */ .highlight .si { color: #a31515 } /* Literal.String.Interpol */ .highlight .sx { color: #a31515 } /* Literal.String.Other */ .highlight .sr { color: #a31515 } /* Literal.String.Regex */ .highlight .s1 { color: #a31515 } /* Literal.String.Single */ .highlight .ss { color: #a31515 } /* Literal.String.Symbol */ ================================================ FILE: designers-guide.md ================================================ --- layout: default title: Designer's Guide --- # Designer's Guide Start with the [Developer's Guide]({{ site.baseurl }}/developers-guide) to get a test version of the cookbook up and running on your machine, and get started! If you have been waiting for your chance to literally _write the designer's guide_ for a website that is also a cookbook about CoffeeScript... ...welcome home. The default color scheme is based on [Solarized](http://ethanschoonover.com/solarized) by Ethan Schoonover. ================================================ FILE: developers-guide.md ================================================ --- layout: default title: Developer's Guide --- # Developer's Guide _Please help out by updating this page_ ### Operating System It works on Mac OS X. Probably works without any changes or issues on Linux. A masochist could probably get it working on Windows. ## Installation ### Clone the repo {% highlight bash %} git clone git://github.com/coffeescript-cookbook/coffeescript-cookbook.github.com.git {% endhighlight %} ### Ruby environment You probably want to have [RVM](http://rvm.io/) installed. The project includes a `.ruby-version` file locked to *1.9.3* since that is what Github Pages are currently using. There is also a `.ruby-gemset` that is set to *coffeescript-cookbook* ### Required dependencies We are using [Bundler](http://bundler.io/) to install the required Ruby dependencies. {% highlight bash %} bundle install {% endhighlight %} #### Install pygments You'll need python installed for this. Macs and most Linuces come with it preinstalled. {% highlight bash %} easy_install pygments # for syntax highlighting {% endhighlight %} ## Building and Viewing the Website Open a terminal window, cd into the project folder and run `foreman start` from the project root. {% highlight bash %} foreman start {% endhighlight %} Leave this window running while you work. Any time you change a file, jekyll will rerender it into the `_site` folder. Open a browser and visit and you should see the site. ## Minutiae and Other Trivialities Jekyll can take a second or two to catch up when you save a file. If you edit a file and don't see the changes in your browser, give it a second or two and try again. As long as it prints `Successfully generated site` you should be alright. ================================================ FILE: index.html ================================================ --- layout: default title: Home ---

    Welcome to the CoffeeScript Cookbook!

    CoffeeScript recipes for the community by the community. Head over to the Contribute page and see what you can do to help out!

    ================================================ FILE: js/scripts.js ================================================ /*jshint browser: true, devel: true, debug: true */ (function( window, document, undefined ) { var activePage, position, sidebar, narrowScreen = ( window.getComputedStyle( document.body, ':after' ).getPropertyValue( 'content' ) === 'narrow' ) ? true : false // if it's not the index page and not a narrow screen if ( window.location.pathname.length && !narrowScreen ) { // get the sidebar sidebar = document.getElementById( 'sidebar' ) // query the dom for the active page activePage = document.getElementsByClassName( 'page-active' ) // if an active page was found if ( activePage.length ) { // get the offset position and give a padding of 80px position = activePage[0].offsetTop - 80 // set the scroll position of the sidebar to the new position sidebar.scrollTop = position } } })( window, document ) ================================================ FILE: license.md ================================================ --- layout: default title: License --- coffeescript-cookbook.github.io is licensed under the [Creative Commons Attribution 3.0 Unported (CC BY 3.0)](http://creativecommons.org/licenses/by/3.0/) license. By submitting information to this site you agree to grant this license to all users of the site, and that your editing of the authors page constitutes satisfactory attribution. [LICENSE]({{ site.baseurl }}/LICENSE-CC-BY) ================================================ FILE: recipe-template.md ================================================ --- layout: default title: Recipe Template --- ## Sample recipe template Create a new `my-recipe.md` file and use this text as a start. {% highlight text %} --- layout: recipe title: Title of The Recipe chapter: Chapter Name --- ## Problem You have a problem. ## Solution Do this about it. ## Discussion Here's why. {% endhighlight %} ================================================ FILE: terms-of-use.md ================================================ --- layout: default title: Terms of Use --- ================================================ FILE: wanted-recipes.md ================================================ --- layout: default title: Wanted Recipes --- # Wanted Recipes Here's a list of recipes we think we need. Pick one, implement it, and remove it from the page. Alternately, add a quick note here for a recipe you'd like to see so someone else can add it. In the notes below, "JS" means the recipe is just a simple pass-through to an existing JavaScript method. ## Syntax * Ensuring variables are closed over # with "do" ## Strings * HTML methods # JS .sup(), .sub(), .blink(), .link(url), etc. May not exist in your JS implementation! * substr {% highlight coffeescript %} str.substr(x,y) === str[x..x+y-1] === str[x...x+y] {% endhighlight %} * substring {% highlight coffeescript %} str.substring(x,y) === str.slice(x,y) === str[x..y-1] === str[x...y] {% endhighlight %} * Replacing substrings ## Arrays * Testing every element in an array {% highlight coffeescript %} evens = (x for x in [0..10] by 2) even = (x) -> x % 2 == 0 evens.every even # => true {% endhighlight %} * Detecting presence of matching items in an array {% highlight coffeescript %} [1..10].some (x) -> x % 2 == 0 # => true {% endhighlight %} ## Math * square root # JS Math.sqrt * Constants # JS Math.PI, Math.E * floor, ceil, round # JS Math.floor, Math.ceil, Math.round * Raising a number to a power # JS Math.pow(x, y) * Logarithms # Math.log * Finding the base-n log # Math.log(num) / Math.log(base) * Exponents # Math.exp * Check if a credit card is valid (checksum, Luhn algorithm) ## Functions * Nested functions {% highlight coffeescript %} hypotenuse = (a, b) -> square = (x) -> x * x Math.sqrt(square(a) + square(b)) console.log hypotenuse 3, 4 # => 5 square 5 # ReferenceError: square is not defined {% endhighlight %} * Optional Arguments # use arg? to detect presence: if arg=0, arg? == true {% highlight coffeescript %} foo = (a, b=42, c) -> if c? then a*b*c else a*b [Function] foo 6 # => 252 foo 1, 2 # => 2 foo 1, 2, 3 # => 6 {% endhighlight %} ## Design patterns * Creational Patterns * Abstract Factory * Prototype * Structural Patterns * Adapter * Composite * Facade * Flyweight * Proxy * Behavioral Patterns * Chain of Responsibility * Iterator * Mediator * Observer * State * Template Method * Visitor ## Databases * Couch access * MySQL/PostgreSQL access