├── .nojekyll ├── styles ├── ebook.css ├── epub.css ├── mobi.css ├── pdf.css └── website.css ├── quickstart └── .md ├── figwheel-project └── .md ├── libraries ├── edn.md ├── om.md └── index.md ├── install ├── lein-npm.md ├── plank.md ├── figwheel.md ├── java.md ├── lumo.md ├── leiningen.md ├── lighttable.md ├── cider.md └── chestnut.md ├── lighttable ├── index.md └── configure-keyboard-mappings.md ├── why-clojurescript └── .md ├── browser-devtools.md ├── gitbook ├── gitbook-plugin-toggle-chapters │ ├── toggle.css │ └── toggle.js ├── gitbook-plugin-disqus │ ├── plugin.css │ └── plugin.js ├── images │ ├── favicon.ico │ └── apple-touch-icon-precomposed-152.png ├── fonts │ └── fontawesome │ │ ├── FontAwesome.otf │ │ ├── fontawesome-webfont.eot │ │ ├── fontawesome-webfont.ttf │ │ ├── fontawesome-webfont.woff │ │ └── fontawesome-webfont.woff2 ├── gitbook-plugin-gist │ └── plugin.js ├── gitbook-plugin-heading-anchors │ └── anchor-style.js ├── gitbook-plugin-codeblock-disable-glossary │ └── disable-glossary.js ├── gitbook-plugin-sectionx │ ├── sectionx.css │ └── sectionx.js ├── gitbook-plugin-ga │ └── plugin.js ├── gitbook-plugin-search │ ├── search.css │ ├── search-engine.js │ ├── search.js │ └── lunr.min.js ├── gitbook-plugin-toolbar │ └── buttons.js ├── gitbook-plugin-lunr │ ├── search-lunr.js │ └── lunr.min.js ├── gitbook-plugin-sharing │ └── buttons.js ├── gitbook-plugin-highlight │ └── ebook.css ├── gitbook-plugin-callouts │ └── plugin.css └── gitbook-plugin-fontsettings │ ├── fontsettings.js │ └── website.css ├── related-libraries └── index.html ├── figwheel └── clojurescript-repl.md ├── basic-om-next-project ├── create-project.md └── index.md ├── create-a-project ├── configure-main-project-function.md ├── configure-main-namespace.md ├── index.md ├── add-ring-dependency.md ├── run-webserver.md ├── coersing-types-and-java-lang.md └── add-a-webserver-function.md ├── images ├── chestnut.png ├── leiningen-logo.jpg ├── react-basics.png ├── clojurescript-logo.png ├── clojurescript-react.png ├── computer-opponent.jpg ├── favicon_io │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── apple-touch-icon.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ └── site.webmanifest ├── lein-new-todo-list.png ├── chrome-devtools-dirac.png ├── gametheory-tictactoe.jpg ├── clojurescript-all-things.png ├── clojurescript-platforms.png ├── shouter-web-ui-example.png ├── chrome-devtools-dirac-fork.png ├── cljs-website-run--webpage.png ├── clojure-practicalli-banner.png ├── clojure-primatives-common.png ├── javascript-the-good-parts.jpg ├── web-development-landscape.png ├── clojurescript-circle-ci-webapp.png ├── clojurescript-react-native-logo.png ├── clojurex-project-bootsrap-panel.png ├── reagent-tictactoe-game-example.png ├── chrome-devtools-inline-source-maps.png ├── clojurescript-interop-code-transit.png ├── clojurescript-skeptics-real-world.png ├── clojurescript-the-mobile-web-sucks.png ├── tictactoe-game-board-dev-console.png ├── cljs-website-run-spacemacs-build-dev.png ├── cljs-website-run-spacemacs-repl-type.png ├── clojurescript-cljs-packages-examples.png ├── clojurescript-community-github-stars.png ├── clojurescript-reframe-stack-concept.png ├── clojurescript-skeptics-clojure-conj.png ├── clojurescript-source-maps-debugging.png ├── clojurex-project-bootstrap-jumbotron.png ├── purelyfunctional-tv-reframe-overview.png ├── clojurescript-its-for-single-page-apps.png ├── practicalli-clojurescript-book-banner.png ├── web-design-bootstrap-jumbotron-clojurex.png ├── cljs-website-run--webpage-live-reloading.png ├── clojurescript-google-closure-product-use.png ├── clojurescript-modules-code-optiomisation.png ├── clojurescript-skeptics-javascript-tooling.png ├── clojurescript-web-technologies-comparison.png ├── clojurescript-what-if-this-was-javascript.png ├── clojure-persistent-data-structures-sharing.png ├── clojurex-project-multiple-sessions-no-styles.png ├── lumo-clojure-repl-startup-times-comparison.png ├── clojurescript-reagent-tictactoe-create-project.png ├── clojurescript-web-technology-comparison-jquery.png ├── cljs-website--github-pages-source-docs-on-master.png ├── clojurescript-google-clojure-compiler-code-size.png ├── clojurescript-bloat-thinner-than-jquery-immutablejs.png ├── clojurescript-javascript-operator-precedence-table.png ├── clojurex-project-mutliple-sessions-style-container.png ├── clojurescript-project-reagent-tictactoe--js-alert-test.png ├── clojurescript-project-reagent-tictactoe--render-board.png ├── chrome-devtools-clojurescript-data-structure-formatting.png ├── clojurescript-project-reagent-tictactoe-new-project-tree.png ├── spacemacs--clojurescript-figwheel-main-select-repl-type.png ├── clojurescript-project-reagent-tictactoe--cli-repl-connected.png ├── clojurescript-project-reagent-tictactoe--git-initial-commit.png ├── clojurescript-project-reagent-tictactoe--js-alert-test-code.png ├── spacemacs--clojurescript-figwheel-main-select-figwheel-main-build.png ├── chrome-devtools-console--react--waringing-array-children-unique-key.png ├── clojurescript-project-reagent-tictactoe--new-project-tree-structure.png ├── clojurescript-project-reagent-tictactoe--game-board--computer-move-basic.png ├── clojurescript-project-reagent-tictactoe--devtools-project-profile-dependency.png ├── clojurescript-project-reagent-tictactoe--devtools-settings-custom-formatters.png ├── clojurescript-project-reagent-tictactoe--create-project-with-figwheel-template.png ├── clojurescript-project-reagent-tictactoe--project-description-and-license-updated.png ├── clojurescript-project-reagent-tictactoe--devtools-console--custom-formatters-not-rendered.png ├── clojurescript-project-reagent-tictactoe--game-board--computer-move-thinking-time-suggestion.png ├── react-logo.svg ├── reactive-traits.svg ├── react-basics.svg ├── clojurescript-platforms.svg └── clojurescript-reframe-stack-concept.svg ├── .gitignore ├── reagent ├── app.md ├── wip.md └── interact-with-the-repl.md ├── reagent-projects └── tic-tac-toe │ ├── clean-the-project.md │ ├── code-review-initial-project.md │ ├── detecting-victory--rows.md │ └── deploy-to-production.md ├── twilight-zone.md ├── deploy-to-gh-pages ├── clojure-syntax ├── data-structures.md └── namespace.md ├── intro-to-clojurescript.org ├── work-in-progress.md ├── overview └── data-types.md ├── README.md └── clojurescript-code └── index.md /.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/ebook.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/epub.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/mobi.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /styles/pdf.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /quickstart/.md: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /figwheel-project/.md: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /libraries/edn.md: -------------------------------------------------------------------------------- 1 | # edn 2 | -------------------------------------------------------------------------------- /libraries/om.md: -------------------------------------------------------------------------------- 1 | # om 2 | -------------------------------------------------------------------------------- /install/lein-npm.md: -------------------------------------------------------------------------------- 1 | # Lein-NPM 2 | -------------------------------------------------------------------------------- /libraries/index.md: -------------------------------------------------------------------------------- 1 | # Libraries 2 | -------------------------------------------------------------------------------- /lighttable/index.md: -------------------------------------------------------------------------------- 1 | # Lighttable 2 | -------------------------------------------------------------------------------- /why-clojurescript/.md: -------------------------------------------------------------------------------- 1 | # Devcards 2 | -------------------------------------------------------------------------------- /browser-devtools.md: -------------------------------------------------------------------------------- 1 | # Browser DevTools 2 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-toggle-chapters/toggle.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /related-libraries/index.html: -------------------------------------------------------------------------------- 1 | # Related Libraries 2 | -------------------------------------------------------------------------------- /figwheel/clojurescript-repl.md: -------------------------------------------------------------------------------- 1 | # ClojureScript REPL 2 | -------------------------------------------------------------------------------- /basic-om-next-project/create-project.md: -------------------------------------------------------------------------------- 1 | # Basic Om-Next project 2 | -------------------------------------------------------------------------------- /lighttable/configure-keyboard-mappings.md: -------------------------------------------------------------------------------- 1 | # Configure Keyboard mappings 2 | -------------------------------------------------------------------------------- /create-a-project/configure-main-project-function.md: -------------------------------------------------------------------------------- 1 | # Configure main project function 2 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-disqus/plugin.css: -------------------------------------------------------------------------------- 1 | #disqus_thread { 2 | margin: 5px 15px; 3 | margin-top: 30px; 4 | } 5 | -------------------------------------------------------------------------------- /images/chestnut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chestnut.png -------------------------------------------------------------------------------- /images/leiningen-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/leiningen-logo.jpg -------------------------------------------------------------------------------- /images/react-basics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/react-basics.png -------------------------------------------------------------------------------- /gitbook/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/images/favicon.ico -------------------------------------------------------------------------------- /images/clojurescript-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-logo.png -------------------------------------------------------------------------------- /images/clojurescript-react.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-react.png -------------------------------------------------------------------------------- /images/computer-opponent.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/computer-opponent.jpg -------------------------------------------------------------------------------- /images/favicon_io/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/favicon.ico -------------------------------------------------------------------------------- /images/lein-new-todo-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/lein-new-todo-list.png -------------------------------------------------------------------------------- /images/chrome-devtools-dirac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chrome-devtools-dirac.png -------------------------------------------------------------------------------- /images/gametheory-tictactoe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/gametheory-tictactoe.jpg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | ._* 4 | *~ 5 | *# 6 | .#* 7 | _book 8 | node_modules 9 | *.log 10 | book.pdf 11 | book.mobi 12 | book.epub 13 | -------------------------------------------------------------------------------- /images/clojurescript-all-things.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-all-things.png -------------------------------------------------------------------------------- /images/clojurescript-platforms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-platforms.png -------------------------------------------------------------------------------- /images/favicon_io/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/favicon-16x16.png -------------------------------------------------------------------------------- /images/favicon_io/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/favicon-32x32.png -------------------------------------------------------------------------------- /images/shouter-web-ui-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/shouter-web-ui-example.png -------------------------------------------------------------------------------- /images/chrome-devtools-dirac-fork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chrome-devtools-dirac-fork.png -------------------------------------------------------------------------------- /images/cljs-website-run--webpage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/cljs-website-run--webpage.png -------------------------------------------------------------------------------- /images/clojure-practicalli-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojure-practicalli-banner.png -------------------------------------------------------------------------------- /images/clojure-primatives-common.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojure-primatives-common.png -------------------------------------------------------------------------------- /images/javascript-the-good-parts.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/javascript-the-good-parts.jpg -------------------------------------------------------------------------------- /images/web-development-landscape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/web-development-landscape.png -------------------------------------------------------------------------------- /images/favicon_io/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/apple-touch-icon.png -------------------------------------------------------------------------------- /gitbook/fonts/fontawesome/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/fonts/fontawesome/FontAwesome.otf -------------------------------------------------------------------------------- /images/clojurescript-circle-ci-webapp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-circle-ci-webapp.png -------------------------------------------------------------------------------- /images/clojurescript-react-native-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-react-native-logo.png -------------------------------------------------------------------------------- /images/clojurex-project-bootsrap-panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurex-project-bootsrap-panel.png -------------------------------------------------------------------------------- /images/reagent-tictactoe-game-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/reagent-tictactoe-game-example.png -------------------------------------------------------------------------------- /images/chrome-devtools-inline-source-maps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chrome-devtools-inline-source-maps.png -------------------------------------------------------------------------------- /images/clojurescript-interop-code-transit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-interop-code-transit.png -------------------------------------------------------------------------------- /images/clojurescript-skeptics-real-world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-skeptics-real-world.png -------------------------------------------------------------------------------- /images/clojurescript-the-mobile-web-sucks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-the-mobile-web-sucks.png -------------------------------------------------------------------------------- /images/favicon_io/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/android-chrome-192x192.png -------------------------------------------------------------------------------- /images/favicon_io/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/favicon_io/android-chrome-512x512.png -------------------------------------------------------------------------------- /images/tictactoe-game-board-dev-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/tictactoe-game-board-dev-console.png -------------------------------------------------------------------------------- /images/cljs-website-run-spacemacs-build-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/cljs-website-run-spacemacs-build-dev.png -------------------------------------------------------------------------------- /images/cljs-website-run-spacemacs-repl-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/cljs-website-run-spacemacs-repl-type.png -------------------------------------------------------------------------------- /images/clojurescript-cljs-packages-examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-cljs-packages-examples.png -------------------------------------------------------------------------------- /images/clojurescript-community-github-stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-community-github-stars.png -------------------------------------------------------------------------------- /images/clojurescript-reframe-stack-concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-reframe-stack-concept.png -------------------------------------------------------------------------------- /images/clojurescript-skeptics-clojure-conj.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-skeptics-clojure-conj.png -------------------------------------------------------------------------------- /images/clojurescript-source-maps-debugging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-source-maps-debugging.png -------------------------------------------------------------------------------- /images/clojurex-project-bootstrap-jumbotron.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurex-project-bootstrap-jumbotron.png -------------------------------------------------------------------------------- /images/purelyfunctional-tv-reframe-overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/purelyfunctional-tv-reframe-overview.png -------------------------------------------------------------------------------- /gitbook/fonts/fontawesome/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/fonts/fontawesome/fontawesome-webfont.eot -------------------------------------------------------------------------------- /gitbook/fonts/fontawesome/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/fonts/fontawesome/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /gitbook/fonts/fontawesome/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/fonts/fontawesome/fontawesome-webfont.woff -------------------------------------------------------------------------------- /images/clojurescript-its-for-single-page-apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-its-for-single-page-apps.png -------------------------------------------------------------------------------- /images/practicalli-clojurescript-book-banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/practicalli-clojurescript-book-banner.png -------------------------------------------------------------------------------- /images/web-design-bootstrap-jumbotron-clojurex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/web-design-bootstrap-jumbotron-clojurex.png -------------------------------------------------------------------------------- /reagent/app.md: -------------------------------------------------------------------------------- 1 | # Reagent app 2 | 3 | To get started with Reagent I recommend the [Reagent Rocks tutorial](http://www.mattgreer.org/articles/reagent-rocks/#on-to-reagent) 4 | 5 | -------------------------------------------------------------------------------- /gitbook/fonts/fontawesome/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/fonts/fontawesome/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /gitbook/images/apple-touch-icon-precomposed-152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/gitbook/images/apple-touch-icon-precomposed-152.png -------------------------------------------------------------------------------- /images/cljs-website-run--webpage-live-reloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/cljs-website-run--webpage-live-reloading.png -------------------------------------------------------------------------------- /images/clojurescript-google-closure-product-use.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-google-closure-product-use.png -------------------------------------------------------------------------------- /images/clojurescript-modules-code-optiomisation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-modules-code-optiomisation.png -------------------------------------------------------------------------------- /images/clojurescript-skeptics-javascript-tooling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-skeptics-javascript-tooling.png -------------------------------------------------------------------------------- /images/clojurescript-web-technologies-comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-web-technologies-comparison.png -------------------------------------------------------------------------------- /images/clojurescript-what-if-this-was-javascript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-what-if-this-was-javascript.png -------------------------------------------------------------------------------- /images/clojure-persistent-data-structures-sharing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojure-persistent-data-structures-sharing.png -------------------------------------------------------------------------------- /images/clojurex-project-multiple-sessions-no-styles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurex-project-multiple-sessions-no-styles.png -------------------------------------------------------------------------------- /images/lumo-clojure-repl-startup-times-comparison.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/lumo-clojure-repl-startup-times-comparison.png -------------------------------------------------------------------------------- /images/clojurescript-reagent-tictactoe-create-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-reagent-tictactoe-create-project.png -------------------------------------------------------------------------------- /images/clojurescript-web-technology-comparison-jquery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-web-technology-comparison-jquery.png -------------------------------------------------------------------------------- /images/cljs-website--github-pages-source-docs-on-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/cljs-website--github-pages-source-docs-on-master.png -------------------------------------------------------------------------------- /images/clojurescript-google-clojure-compiler-code-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-google-clojure-compiler-code-size.png -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-gist/plugin.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "jQuery"], function(gitbook, $) { 2 | gitbook.events.bind("page.change", function() { 3 | $('[data-gist-id]').gist(); 4 | }); 5 | }); -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-heading-anchors/anchor-style.js: -------------------------------------------------------------------------------- 1 | anchors.options = { 2 | placement: 'left' 3 | } 4 | 5 | gitbook.events.bind('page.change', function(){ 6 | anchors.add('h1,h2,h3,h4,h5') 7 | }) 8 | -------------------------------------------------------------------------------- /images/clojurescript-bloat-thinner-than-jquery-immutablejs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-bloat-thinner-than-jquery-immutablejs.png -------------------------------------------------------------------------------- /images/clojurescript-javascript-operator-precedence-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-javascript-operator-precedence-table.png -------------------------------------------------------------------------------- /images/clojurex-project-mutliple-sessions-style-container.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurex-project-mutliple-sessions-style-container.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--js-alert-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--js-alert-test.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--render-board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--render-board.png -------------------------------------------------------------------------------- /images/chrome-devtools-clojurescript-data-structure-formatting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chrome-devtools-clojurescript-data-structure-formatting.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe-new-project-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe-new-project-tree.png -------------------------------------------------------------------------------- /images/spacemacs--clojurescript-figwheel-main-select-repl-type.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/spacemacs--clojurescript-figwheel-main-select-repl-type.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--cli-repl-connected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--cli-repl-connected.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--git-initial-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--git-initial-commit.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--js-alert-test-code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--js-alert-test-code.png -------------------------------------------------------------------------------- /images/spacemacs--clojurescript-figwheel-main-select-figwheel-main-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/spacemacs--clojurescript-figwheel-main-select-figwheel-main-build.png -------------------------------------------------------------------------------- /images/chrome-devtools-console--react--waringing-array-children-unique-key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/chrome-devtools-console--react--waringing-array-children-unique-key.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--new-project-tree-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--new-project-tree-structure.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--game-board--computer-move-basic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--game-board--computer-move-basic.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--devtools-project-profile-dependency.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--devtools-project-profile-dependency.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--devtools-settings-custom-formatters.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--devtools-settings-custom-formatters.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--create-project-with-figwheel-template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--create-project-with-figwheel-template.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--project-description-and-license-updated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--project-description-and-license-updated.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--devtools-console--custom-formatters-not-rendered.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--devtools-console--custom-formatters-not-rendered.png -------------------------------------------------------------------------------- /images/clojurescript-project-reagent-tictactoe--game-board--computer-move-thinking-time-suggestion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/practicalli/clojurescript-manual-deployment/gh-pages/images/clojurescript-project-reagent-tictactoe--game-board--computer-move-thinking-time-suggestion.png -------------------------------------------------------------------------------- /images/favicon_io/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /reagent-projects/tic-tac-toe/clean-the-project.md: -------------------------------------------------------------------------------- 1 | # Cleaning up 2 | 3 | If the project is behaving strangely, a quick solution may be to clean the project. This removes anything built from source code and ensures your not picking up older versions. 4 | 5 | To clean all compiled files: 6 | 7 | ```bash 8 | lein clean 9 | ``` 10 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-codeblock-disable-glossary/disable-glossary.js: -------------------------------------------------------------------------------- 1 | require(["gitbook"], function(gitbook) { 2 | gitbook.events.bind("page.change", function() { 3 | $('pre .glossary-term') 4 | .removeAttr('href') 5 | .removeAttr('title') 6 | .removeClass('glossary-term') 7 | .css('text-decoration', 'none'); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /install/plank.md: -------------------------------------------------------------------------------- 1 | # Plank - stand-alone ClojureScript REPL for macOS and Linux 2 | 3 | 4 | Planck launches instantly and is useful for scripting. 5 | 6 | [Setup](http://planck-repl.org/setup.html) 7 | 8 | Get it: On macOS brew install planck, on Ubuntu install using apt-get. 9 | 10 | Read the [Planck User Guide](http://planck-repl.org/guide.html). 11 | 12 | Planck is free and open source. 13 | -------------------------------------------------------------------------------- /basic-om-next-project/index.md: -------------------------------------------------------------------------------- 1 | # Basic Om-Next project 2 | 3 | ## What is Om 4 | 5 | * V in MVC 6 | * Imediate mode rendering 7 | * Components 8 | 9 | ## Updating the DOM 10 | 11 | Om (and react) compares the latest changes to the virtual dom with the previous version of the dom, essentially creating a diff. This diff is then applied to the DOM in the browser. This approach avoids having to query the DOM in the browser which is an incredibly slow process. 12 | 13 | -------------------------------------------------------------------------------- /install/figwheel.md: -------------------------------------------------------------------------------- 1 | # Figwheel 2 | 3 | When you run your project with figwheel, `lein figwheel`, your project is compiled and a JavaScript REPL is started once you connect your browser to figwheel on port `3449`. 4 | 5 | 6 | 7 | Examples: 8 | 9 | * [Interactive programming Flappy Birds in ClojureScript](https://www.youtube.com/watch?v=KZjFVdU8VLI) 10 | * [Clojure West 2015 - Developing ClojureScript with Figwheel](https://www.youtube.com/watch?v=j-kj2qwJa_E) 11 | 12 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-sectionx/sectionx.css: -------------------------------------------------------------------------------- 1 | .book.color-theme-2 .panel { 2 | background: #1A1A1A; 3 | border-style: solid; 4 | border-color: rgba(0,0,0,0.1); 5 | border-width: 1px; 6 | } 7 | 8 | .book.color-theme-2 .panel-heading { 9 | background-color: #2D3143; 10 | border-width: 0px; 11 | } 12 | 13 | .book.color-theme-2 .panel-body { 14 | background: #1A1A1A; 15 | } 16 | 17 | .book.color-theme-2 a.section { 18 | border-color: #373b4e !important; 19 | } 20 | 21 | .atTitle { 22 | display: flex; 23 | } 24 | -------------------------------------------------------------------------------- /reagent-projects/tic-tac-toe/code-review-initial-project.md: -------------------------------------------------------------------------------- 1 | # Tic-tac-toe: Code Review - Initial Project 2 | 3 | Lets take a quick look at the project files to see what the figwheel template created when we ran `lein new figwheel tictactoe-reagent -- --reagent` 4 | 5 | ## Project configuration 6 | 7 | The `project.clj` contains all the configuration for the ClojureScript project. This included the libraries (dependencies) that the project uses. 8 | 9 | 10 | 11 | 12 | ## Main ClojureScript file 13 | 14 | The `src/tictactoe-reagent/core.cljs` file contains the code that the project will call when run. 15 | -------------------------------------------------------------------------------- /twilight-zone.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | You unlock this door with the key of imagination, beyond it is another dimention 4 | a dimension of sound (sound design of a language) 5 | a dimension of sight (the repl lets you see clearly what is going on in your code) 6 | a dimention of mind (thinking functionally, thinking about pure functions and immutablility, thinking about data) 7 | 8 | Your moving into a land of both shadow and substance (javascript & Clojurescript) 9 | of things and ideas (data structures and functions) 10 | 11 | you just crossed over to the Clojurescript Zone... 12 | 13 | The boundaries are only your imagination 14 | -------------------------------------------------------------------------------- /images/react-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /create-a-project/configure-main-namespace.md: -------------------------------------------------------------------------------- 1 | # Configure main namespace 2 | 3 | > **Note** Set the default namespace for your Clojure project. 4 | 5 | Edit the `project.clj` file and add `:main todo-list.core` configuration option. 6 | 7 | Setting the default namespace will automatically call a function called `-main` when the Clojure project is run, i.e. via `lein run` 8 | 9 | ```clojure 10 | (defproject todo-list "0.1.0-SNAPSHOT" 11 | :description "A simple webapp using Ring" 12 | :url "http://example.com/FIXME" 13 | :license {:name "Eclipse Public License" 14 | :url "http://www.eclipse.org/legal/epl-v10.html"} 15 | :dependencies [[org.clojure/clojure "1.7.0"] 16 | [ring "1.4.0"]] 17 | :main todo-list.core) 18 | ``` 19 | 20 | Now you are ready to run your web server. 21 | 22 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-ga/plugin.js: -------------------------------------------------------------------------------- 1 | require(["gitbook"], function(gitbook) { 2 | // Load analytics.js 3 | gitbook.events.bind("start", function(e, config) { 4 | (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ 5 | (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), 6 | m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) 7 | })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); 8 | 9 | var cfg = config.ga; 10 | ga('create', cfg.token, cfg.configuration); 11 | }); 12 | 13 | // Notify pageview 14 | gitbook.events.bind("page.change", function() { 15 | ga('send', 'pageview', window.location.pathname+window.location.search); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-toggle-chapters/toggle.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "jQuery"], function(gitbook, $) { 2 | 3 | function expand(chapter) { 4 | chapter.show(); 5 | if (chapter.parent().attr('class') != 'summary' 6 | && chapter.parent().attr('class') != 'book-summary' 7 | && chapter.length != 0 8 | ) { 9 | expand(chapter.parent()); 10 | } 11 | } 12 | 13 | gitbook.events.bind("page.change", function() { 14 | $('li.chapter').children('ul.articles').hide(); 15 | $chapter = $('li.chapter.active'); 16 | $children = $chapter.children('ul.articles'); 17 | $parent = $chapter.parent(); 18 | $siblings = $chapter.siblings().children('ul.articles'); 19 | 20 | expand($chapter); 21 | 22 | if ($children.length > 0) { 23 | $children.show(); 24 | } 25 | }); 26 | 27 | }); 28 | -------------------------------------------------------------------------------- /reagent-projects/tic-tac-toe/detecting-victory--rows.md: -------------------------------------------------------------------------------- 1 | # Dectecting Victory - rows only 2 | 3 | A function that will test to see if a row has a winning line 4 | 5 | ```clojure 6 | (defn winning-row 7 | "Return the winner, :nought or :cross if there is a winning row, otherwise return false" 8 | [cell-row] 9 | (if (and (apply = cell-row) 10 | (apply #(not= :empty %) cell-row)) 11 | (first cell-row) 12 | false)) 13 | ``` 14 | 15 | Lets add something simple to check if the first row is a winner 16 | 17 | ```clojure 18 | (defn check-for-winner 19 | "Checks the app-state to see if there is a current winner" 20 | [] 21 | (let [winner (winning-row (first (@app-state :board)))] 22 | (prn "The winner is:" winner) 23 | winner)) 24 | ``` 25 | 26 | Now lest update check-for-winner to try all rows. We will assume that there will be only one winning row. 27 | -------------------------------------------------------------------------------- /reagent-projects/tic-tac-toe/deploy-to-production.md: -------------------------------------------------------------------------------- 1 | ## Production builds 2 | 3 | To create a production build, run: 4 | 5 | ```bash 6 | lein do clean, cljsbuild once min 7 | ``` 8 | 9 | Open your browser in `resources/public/index.html`. You will not get live reloading, nor a REPL. 10 | 11 | 12 | > ####Hint::What does a production build do 13 | > Creating a production build in this way Minifys the resulting JavaScript code, reducing names of vars and function to a minimum number of characters. This result in a much smaller JavaScript file which takes a little less time to be transmitted. 14 | > 15 | > The production build also carries out Dead Code analysis, where any code that is never called is simply removed from the resulting JavaScript. 16 | > 17 | > Both these actions result in a much faster execution of the JavaScript code, resulting in a better experience in the web browser. 18 | -------------------------------------------------------------------------------- /deploy-to-gh-pages: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # A quick hack to deploy a generated website onto a Github repository, to be served as static content by github pages. 4 | 5 | # This should only be run from the root of the gitbook project 6 | 7 | # Trigger a clean and new build 8 | gitbook build . 9 | 10 | # Prevent Jekyll processing when pushing commits to GitHub 11 | touch _book/.nojekyll 12 | 13 | # Add Practicalli favicon 14 | cp -f images/favicon_io/favicon.ico _book/gitbook/images/favicon.ico 15 | 16 | # Add Apple touch icon 17 | cp -f images/favicon_io/apple-touch-icon.png _book/gitbook/images/apple-touch-icon-precomposed-152.png 18 | 19 | 20 | # Deploy new build 21 | cd _book && rm -rf .git && git init && git add . && git commit -m "Publish new version of book" && git branch -m live gh-pages && git remote add practicalli git@github.com:practicalli/clojurescript.git && git push -f practicalli gh-pages 22 | -------------------------------------------------------------------------------- /clojure-syntax/data-structures.md: -------------------------------------------------------------------------------- 1 | # Data Structures 2 | 3 | 4 | When you define a name for a persistent data structure (list, map, vector, set), that name is termed a `Var`. A var is a mutable reference that points to the data structure. A Var is mutable, because it can be changed to point to another data structure (or any other legal Clojure expression) 5 | 6 | When you pass a name as an argument to a function, you are passing a reference to a data structure. If that function modifies that data structure, it will create a new data structure and pass that back by value. 7 | 8 | The new data structure may include references to the original data structure if they have common values, an in-memory sharing model that makes persistent data structures very efficient in terms of memory use. 9 | 10 | Persistent data structures are held in memory as binary tree of values - TODO: how does this look for the different persistent data structures (eg. is the node value for a map the key & value) 11 | -------------------------------------------------------------------------------- /install/java.md: -------------------------------------------------------------------------------- 1 | ## Java 2 | 3 | You will need to have a Java Runtime Edition (usually installed on most computers by default) to run any Clojure applications. 4 | 5 | Check you have a Java runtime on your system path by typing the following in a terminal window: 6 | 7 | ```bash 8 | java -version 9 | ``` 10 | 11 | ## Install Java 12 | 13 | If Java is not found, please install it from [Oracle](http://www.oracle.com/technetwork/java/javase/downloads/index.html) or [OpenJDK](http://openjdk.java.net/projects/jdk8/). 14 | 15 | Java may also be available via your operating systems package manger, for example on Ubuntu / Debian systems this would be installed via sudo apt-get install openjdk-8-jre` 16 | 17 | 18 | > **Hint** ClojureScript uses the Google Closure compiler and build tools that require a Java Virtual machine. However, work is in progress to make ClojureScript self hosting. See articles such as [ClojureScript Next](http://swannodette.github.io/2015/07/29/clojurescript-17/) and [Bootstrapped ClojureScript FAQ](https://github.com/clojure/clojurescript/wiki/Bootstrapped-ClojureScript-FAQ) for pro's and con's of this approach. 19 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-search/search.css: -------------------------------------------------------------------------------- 1 | /* 2 | This CSS only styled the search results section, not the search input 3 | It defines the basic interraction to hide content when displaying results, etc 4 | */ 5 | #book-search-results .search-results { 6 | display: none; 7 | } 8 | #book-search-results .search-results ul.search-results-list { 9 | list-style-type: none; 10 | padding-left: 0; 11 | } 12 | #book-search-results .search-results ul.search-results-list li { 13 | margin-bottom: 1.5rem; 14 | padding-bottom: 0.5rem; 15 | /* Highlight results */ 16 | } 17 | #book-search-results .search-results ul.search-results-list li p em { 18 | background-color: rgba(255, 220, 0, 0.4); 19 | font-style: normal; 20 | } 21 | #book-search-results .search-results .no-results { 22 | display: none; 23 | } 24 | #book-search-results.open .search-results { 25 | display: block; 26 | } 27 | #book-search-results.open .search-noresults { 28 | display: none; 29 | } 30 | #book-search-results.no-results .search-results .has-results { 31 | display: none; 32 | } 33 | #book-search-results.no-results .search-results .no-results { 34 | display: block; 35 | } 36 | -------------------------------------------------------------------------------- /create-a-project/index.md: -------------------------------------------------------------------------------- 1 | # Create a project 2 | 3 | Lets create a project called `todo-list` using Leiningen, the build automation tool for Clojure. This project will run the simplest possible webserver. 4 | 5 | On the command line: 6 | 7 | ```bash 8 | lein new todo-list 9 | ``` 10 | 11 | ![Leiningen - new project called todo-list](../images/lein-new-todo-list.png) 12 | 13 |
14 | 15 | ## Take a look at the project structure 16 | 17 | To see how our project is layed out, change into the `todo-list` directory created by this command and see the project structure that has been created. 18 | 19 | * `project.clj` - the project definition, written in Clojure 20 | * `src` for all the source code 21 | * `test` for unit test code 22 | 23 | 24 | Here is an example of what our project looks like using the `tree` command (you could use `ls -R` or a graphical file browser if you wish) 25 | 26 | ![Clojure project structure - webdev](/images/project-todo-list-tree.png) 27 | 28 | 29 | > **Hint** Look closer at the directory hierachy and you will see something has happend to our `todo-list` name. Unfortunately Java does not like dashes '-' in directory or file names, so Leiningen changes the directory names to `src/todo_list/co` & `test/todo_list` and the initial test to `src/todo_list/core_test.clj`. 30 | 31 | -------------------------------------------------------------------------------- /reagent/wip.md: -------------------------------------------------------------------------------- 1 | # Work In Progress 2 | 3 | 4 | ## Do not deref inside lazy sequences 5 | 6 | Hey, any idea why this doesn't work? Clicking on the button doesn't update the component 7 | 8 | ```clojure 9 | (defn root-component [] 10 | (let [selected (r/atom 0)] 11 | (fn [] 12 | [:p [:button {:on-click #(reset! selected 1)} "Click me"] 13 | (map 14 | (fn [x] (str (= x @selected))) 15 | [0 1 2])]))) 16 | 17 | ``` 18 | However, this works and update the component correctly: 19 | 20 | 21 | ```clojure 22 | (defn root-component [] 23 | (let [selected (r/atom 0)] 24 | (fn [] 25 | [:p [:button {:on-click #(reset! selected 1)} "Click me"] 26 | (str (map 27 | (fn [x] (= x @selected)) 28 | [0 1 2]))]))) 29 | ``` 30 | 31 | (note the difference of where I put the str function) 32 | 33 | dereferencing the selected atom inside of a lazy sequence, so reagent isn’t able to capture the dereference and set up its tracking magic for you 34 | if you lift the dereference out of the map you should see it work: 35 | 36 | ```clojure 37 | (defn root-component [] 38 | (let [selected (r/atom 0)] 39 | (fn [] 40 | [:p [:button {:on-click #(reset! selected 1)} "Click me"] 41 | (let [selected-val @selected] 42 | (map 43 | (fn [x] (str (= x selected-val))) 44 | [0 1 2])])))) 45 | ``` 46 | -------------------------------------------------------------------------------- /create-a-project/add-ring-dependency.md: -------------------------------------------------------------------------------- 1 | # Add Ring Dependency 2 | 3 | As we are using the Ring library for our Clojure project we need to add that library as a dependency. Just like other build tools (i.e. Maven, Gradle) we include this dependency in our build file, `project.clj` 4 | 5 | > **Note** Edit the `project.clj` file and add the ring dependency. For bonus points write a simple description of the project 6 | 7 | ```clojure 8 | (defproject todo-list "0.1.0-SNAPSHOT" 9 | :description "A simple webapp using Ring" 10 | :url "http://example.com/FIXME" 11 | :license {:name "Eclipse Public License" 12 | :url "http://www.eclipse.org/legal/epl-v10.html"} 13 | :dependencies [[org.clojure/clojure "1.7.0"] 14 | [ring "1.4.0"]]) 15 | ``` 16 | 17 | > **Hint** Read the [dependencies secion of the Leiningen documentation](https://github.com/technomancy/leiningen/blob/stable/doc/TUTORIAL.md#dependencies) to learn more about adding libraries. 18 | 19 | ## Looking up Libraries & current versions 20 | 21 | There are a large number of Clojure libraries available via Clojars.org, an online repository similar to Maven Central. 22 | 23 | To look up the latest version of a library, visit the Clojars.org website and search for the library name. 24 | 25 | 26 | ![](/images/clojure-webdev-clojars-ring.png) 27 | 28 | The dependency notation for Leiningen and Maven are documented for each library. 29 | -------------------------------------------------------------------------------- /install/lumo.md: -------------------------------------------------------------------------------- 1 | # Lumo - Fast, cross-platform, standalone ClojureScript environment 2 | 3 | Read the [announcement blog post](https://anmonteiro.com/2016/11/the-fastest-clojure-repl-in-the-world/). 4 | 5 | ## Contents 6 | 7 | - [Installation](#installation) 8 | - [Using Lumo](#using-lumo) 9 | - [Building](#building) 10 | - [Copyright & License](#copyright--license) 11 | 12 | ## Installation 13 | 14 | ### Via [NPM](https://www.npmjs.com/package/lumo-cljs) 15 | 16 | ```shell 17 | $ npm install -g lumo-cljs 18 | ``` 19 | 20 | Note: the installed binary will be named `lumo` rather than `lumo-cljs`_ 21 | 22 | ### Via [Homebrew](http://brew.sh/) (macOS) 23 | 24 | ```shell 25 | $ brew install lumo 26 | ``` 27 | 28 | **Note:** If you want to install a binary built from master, run `brew install --HEAD lumo` 29 | (at your own responsibility). 30 | 31 | ### Manual 32 | 33 | 1. Download the [latest release](https://github.com/anmonteiro/lumo/releases/latest). 34 | 2. Move it to somewhere in your `$PATH`. 35 | 36 | ## Using Lumo 37 | 38 | Enter `lumo` at the command line to launch it. 39 | 40 | Check out `lumo -h` for usage instructions and supported command line options. 41 | 42 | ## Building 43 | 44 | To build Lumo from source: 45 | 46 | 1. Make sure you have installed [Boot](http://boot-clj.com/) and [Yarn](https://yarnpkg.com/). 47 | 2. At the root of the repository, run: `boot release`. 48 | 3. The resulting binary can be found in `build/lumo` (or `build\lumo.exe` if you're 49 | on Windows). 50 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-search/search-engine.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'gitbook', 3 | 'jquery' 4 | ], function(gitbook, $) { 5 | // Global search objects 6 | var engine = null; 7 | var initialized = false; 8 | 9 | // Set a new search engine 10 | function setEngine(Engine, config) { 11 | initialized = false; 12 | engine = new Engine(config); 13 | 14 | init(config); 15 | } 16 | 17 | // Initialize search engine with config 18 | function init(config) { 19 | if (!engine) throw new Error('No engine set for research. Set an engine using gitbook.research.setEngine(Engine).'); 20 | 21 | return engine.init(config) 22 | .then(function() { 23 | initialized = true; 24 | gitbook.events.trigger('search.ready'); 25 | }); 26 | } 27 | 28 | // Launch search for query q 29 | function query(q, offset, length) { 30 | if (!initialized) throw new Error('Search has not been initialized'); 31 | return engine.search(q, offset, length); 32 | } 33 | 34 | // Get stats about search 35 | function getEngine() { 36 | return engine? engine.name : null; 37 | } 38 | 39 | function isInitialized() { 40 | return initialized; 41 | } 42 | 43 | // Initialize gitbook.search 44 | gitbook.search = { 45 | setEngine: setEngine, 46 | getEngine: getEngine, 47 | query: query, 48 | isInitialized: isInitialized 49 | }; 50 | }); -------------------------------------------------------------------------------- /create-a-project/run-webserver.md: -------------------------------------------------------------------------------- 1 | # Run webserver 2 | 3 | > **Note** Run the webserver we use Leiningen, the Clojure build automation tool. In the root of your Clojure project, the directory that contains `project.clj` 4 | 5 | In a command line terminal, navigate to the root of your project and type the following command 6 | 7 | ```bash 8 | lein run 8000 9 | ``` 10 | 11 | This command should start up a Jetty web server that listens on http://localhost:8000. 12 | 13 | ![](../images/todo-list-lein-run-portnumber.png) 14 | 15 | > **Note** Open http://localhost:8000 in your browser and try out different pages, such at [/hello]( http://localhost:8000/hello), [/goodbye]( http://localhost:8000/goodbye) or [/complete-indifference]( http://localhost:8000/complete-indifference). It should not matter what page you visit, you should get the same response. 16 | 17 | ![todo-list project in the browser](../images/todo-list-lein-run-browser.png) 18 | 19 | --- 20 | 21 | ## The project so far 22 | 23 | The code and configuration we have created so far are in the [clojure-webapps-example](https://github.com/practicalli/clojure-webapps-example) github repository, specifically the branch called `01-create-a-webserver` 24 | 25 | If something is not working or you want to speed up, simply clone the project into a new directory using the command: 26 | 27 | ```bash 28 | git clone https://github.com/practicalli/clojure-webapps-example 29 | ``` 30 | Once you have cloned the project, checkout the `01-create-a-webserver` branch 31 | 32 | ``` 33 | git checkout 01-create-a-webserver 34 | ``` 35 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-toolbar/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook'], function(gitbook) { 2 | 3 | gitbook.events.bind('start', function(e, config) { 4 | var opts = config.toolbar; 5 | 6 | if (!opts || !opts.buttons) return; 7 | 8 | var buttons = opts.buttons.slice(0); 9 | buttons.reverse(); 10 | 11 | buttons.forEach(function(button) { 12 | gitbook.toolbar.createButton({ 13 | icon: button.icon || "fa fa-external-link", 14 | label: button.label || "Link", 15 | position: button.position || "right", 16 | onClick: function(e) { 17 | e.preventDefault(); 18 | var mapping = { 19 | "{{title}}": encodeURIComponent(document.title), 20 | "{{url}}": encodeURIComponent(location.href), 21 | "{{filepath_lang}}": encodeURIComponent((gitbook.state.innerLanguage ? gitbook.state.innerLanguage+'/' : '')+gitbook.state.filepath) 22 | }; 23 | var re = RegExp(Object.keys(mapping).join("|"), "g"); 24 | var url = button.url.replace(re, function(matched) { 25 | return mapping[matched]; 26 | }); 27 | if (button.target == "_self") { 28 | window.location = url; 29 | } else { 30 | window.open(url, button.target || "_blank"); 31 | } 32 | } 33 | }); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /install/leiningen.md: -------------------------------------------------------------------------------- 1 | # Leiningen Build tool 2 | 3 | [Leiningen](http://leiningen.org/) (pronounced line-ing) is the build automation tool used to manage Clojure projects. Features include: 4 | 5 | * Creating Clojure Projects 6 | * Dependency Management 7 | * Running browser-REPL interactive environment (figwheel plugin) 8 | 9 |
10 | ![Leiningen - build automation for Clojure](../images/leiningen-repl-custom-prompt.png) 11 |
12 | 13 | ## Install Leiningen 14 | 15 | Install Leiningen by saving the Leiningen install script to somewhere on your operating system path, eg `~/bin`and then running that script from the command line. 16 | 17 | * [Install script for Linux & MacOSX](https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein) 18 | * [Install script for Microsoft Windows](https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein.bat) 19 | 20 | 21 | On Linux and MacOSX, make the script executable first and then run the `lein` script 22 | 23 | ```bash 24 | chmod a+x ~/bin/lein 25 | lein 26 | ``` 27 | 28 | > **hint** I create a `~/bin` directory and add it to my operating system execution path ($PATH), placing the `lein` script in `~/bin` so I can call it from anywhere on the filesystem. 29 | 30 | 31 | The first time you run this script it downloads a Java archive file (JAR) of the latest version of Leiningen. When you run the script again, you have a working Leiningen build tool. 32 | 33 | ## Testing Leiningen 34 | 35 | Test that Leiningen is installed with the following command 36 | 37 | lein version 38 | 39 | Output should look similar to: 40 | 41 | Leiningen 2.7.1 on Java 1.8.0 Java HotSpot(TM) 64-Bit Server VM 42 | -------------------------------------------------------------------------------- /install/lighttable.md: -------------------------------------------------------------------------------- 1 | # LightTable 2 | 3 | This workshop will use LightTable to help you discover Clojure. 4 | 5 | I have found LightTable to be one of the best tools to help you learn Clojure, especially the highly interactive Instarepl that evaluates your Clojure code as you type. 6 | 7 | LightTable is a modern development tool that supports Clojure, ClojureScript, JavaScript and Python languages. The tool is open source and written in Clojure & ClojureScript (with a little JavaScript & CSS) 8 | 9 | ![](../images/lighttable-screens.png) 10 | 11 | > **Note** Install Lighttable from [lighttable.com](http://lighttable.com) 12 | 13 | **Linux** 14 | Extract the contents of the downloaded lighttable file to a suitable directory (`/usr/local` or `/opt` or `~/apps`). Make sure the `LightTable` file in the extracted folder is accessible via the system `$PATH`. 15 | 16 | **MacOSX** 17 | Install the `lighttable.dmg` file just as any other MacOSX package 18 | 19 | **Windows** 20 | Download the windows zip file for LightTable and extract the installer, following the instructions inside the installer. 21 | 22 | ## Using LightTable 23 | 24 | LightTable has an online tutorial entitled [Getting started with LightTable](http://docs.lighttable.com/tutorials/full/) 25 | 26 | > **Comment** I mainly use the Instarepl in LightTable. I create a project first with Leiningen, open the project directory in the LightTable workspace and oopen any files I want to work with. I then connect the open editor window for the file to an Instarepl. 27 | 28 | --- 29 | 30 | > **fixme** my approach is documented in the [quick demo section of my Clojure & LightTable slides](http://jr0cket.co.uk/slides/jax-london-2013-light-table.html#/sec-12) from JAXLondon 2013. This should be pulled into this workshop into its own section 31 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-lunr/search-lunr.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'gitbook', 3 | 'jquery' 4 | ], function(gitbook, $) { 5 | // Define global search engine 6 | function LunrSearchEngine() { 7 | this.index = null; 8 | this.store = {}; 9 | this.name = 'LunrSearchEngine'; 10 | } 11 | 12 | // Initialize lunr by fetching the search index 13 | LunrSearchEngine.prototype.init = function() { 14 | var that = this; 15 | var d = $.Deferred(); 16 | 17 | $.getJSON(gitbook.state.basePath+'/search_index.json') 18 | .then(function(data) { 19 | // eslint-disable-next-line no-undef 20 | that.index = lunr.Index.load(data.index); 21 | that.store = data.store; 22 | d.resolve(); 23 | }); 24 | 25 | return d.promise(); 26 | }; 27 | 28 | // Search for a term and return results 29 | LunrSearchEngine.prototype.search = function(q, offset, length) { 30 | var that = this; 31 | var results = []; 32 | 33 | if (this.index) { 34 | results = $.map(this.index.search(q), function(result) { 35 | var doc = that.store[result.ref]; 36 | 37 | return { 38 | title: doc.title, 39 | url: doc.url, 40 | body: doc.summary || doc.body 41 | }; 42 | }); 43 | } 44 | 45 | return $.Deferred().resolve({ 46 | query: q, 47 | results: results.slice(0, length), 48 | count: results.length 49 | }).promise(); 50 | }; 51 | 52 | // Set gitbook research 53 | gitbook.events.bind('start', function(e, config) { 54 | var engine = gitbook.search.getEngine(); 55 | if (!engine) { 56 | gitbook.search.setEngine(LunrSearchEngine, config); 57 | } 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-sectionx/sectionx.js: -------------------------------------------------------------------------------- 1 | require(["gitbook", "jquery"], function (gitbook, $) { 2 | 3 | var sectionToggle = function (tar, button) { 4 | var $target = $('#' + tar); 5 | $target.collapse('toggle'); 6 | if (button) 7 | $target.parents('.panel').toggle('slow'); 8 | }; 9 | 10 | var clickAction = function ($source, tar) { 11 | $source.click(function () { 12 | sectionToggle(tar, !$(this).hasClass('atTitle')); 13 | if (!$(this).hasClass('atTitle')) 14 | $(this).toggleClass('btn-info').toggleClass('btn-success'); 15 | }); 16 | 17 | $('#' + tar).on('show.bs.collapse', function () { 18 | $source.html($source.attr('hide') ? 19 | ('' + $source.attr('hide') + '') : 20 | ''); 21 | }); 22 | 23 | $('#' + tar).on('hide.bs.collapse', function () { 24 | $source.html($source.attr('show') ? 25 | ('' + $source.attr('show') + '') : ''); 26 | }); 27 | }; 28 | 29 | gitbook.events.bind("page.change", function () { 30 | $('sec').each(function () { 31 | if ($(this).find('.panel').hasClass('hidden')) 32 | $(this).find('.panel').removeClass('hidden').hide(); 33 | if ($(this).data('collapse') === true) { 34 | $('#' + $(this).data('id')).collapse('hide'); 35 | } 36 | //.collapse('toggle'); 37 | }); 38 | 39 | $('.section').each(function () { 40 | clickAction($(this), $(this).attr('target')); 41 | if (!$(this).hasClass('atTitle')) { 42 | $(this).addClass('btn btn-info'); 43 | $(this).html($(this).attr('show') ? 44 | ('' + $(this).attr('show') + '') : 45 | ''); 46 | } 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /intro-to-clojurescript.org: -------------------------------------------------------------------------------- 1 | * Introduction to Clojurescript 2 | - By: Jon Pither, JUXT 3 | - Twiiter: @jonpither 4 | 5 | 6 | ** Why clojurescript 7 | We want to build a rich experience with our apps that give a rewarding user experience 8 | 9 | Lots of languages compile to Javascript now, wo why not Clojure 10 | 11 | 12 | 13 | 14 | ** The project 15 | 16 | Clojurescript is compiled into `public/index.html` 17 | 18 |
Where Clojurescript fills in the blanks
19 | 20 | in app.cljs 21 | 22 | route function 23 | 24 | ** dommy 25 | wraps the dom so you can play around with it 26 | 27 | * Reagent 28 | - Malcom Sparks 29 | 30 | ** Reagent atom 31 | 32 | ** Reframe 33 | An idiom on top of reagent 34 | - single app state (single map) 35 | - state flows through your application 36 | - event dispatch facility is a back channel to update your app state 37 | 38 | ** Modularity 39 | 40 | * Clojurescript project ideas 41 | 42 | ** Clojure Trivia 43 | Build a game that has a series of cards, each with a question hidden on the reverse side of the question. 44 | 45 | When you click on a card the question is revealed. Then the audience has a chance to answer the question. 46 | 47 | When the card is clicked again, the card shows the answer. 48 | 49 | When the card is clicked for a third time the card is marked as answered and the card is shaded. 50 | 51 | Additional aspects 52 | - add a timer to answer the question 53 | - mark if the card was answered correctly 54 | - group cards by topic & show the topic in one column 55 | - assign values to each card, eg, 50, 100, 200, 500, 1000 56 | -- the higher the card value the harder the question should be 57 | - total points earned by a player is recorded and shown 58 | - add players to the game 59 | - players take turns answering questions 60 | -- if a player answers a question correctly they have another turn 61 | 62 | *** Example projects to help 63 | - Parens of the dead - a tile matching game 64 | -------------------------------------------------------------------------------- /work-in-progress.md: -------------------------------------------------------------------------------- 1 | # CSS 2 | ## SVG 3 | ### z-index experiements 4 | https://codepen.io/GarySiu/pen/YBqxjL 5 | ## Flexbox layout 6 | https://css-tricks.com/snippets/css/a-guide-to-flexbox/ 7 | https://flexboxfroggy.com/ 8 | 9 | 10 | 11 | 12 | # Github 13 | ## Github code for the workshop 14 | 15 | The code for this workshop is contained in the Github repository [ClojureScript example](https://github.com/practicalli/clojure-webapps-example), the code for each section is in a specific branch. 16 | 17 | To get a copy of the repository, use the following git clone command which creates a new directory called clojure-webapps-example that contains the cloned code. 18 | 19 | `git clone https://github.com/practicalli/clojurescript-example.git` 20 | 21 | Once you have the repository, use `git checkout branch-name` to get the code for each section. Each branch is a working application with all the features covered in that section 22 | 23 | Use `git branch` to show all the branches available, they should match the names of the sections in this workshop. 24 | 25 | Enjoy. 26 | 27 | 28 | 29 | 30 | ## Deploying to Heroku 31 | 32 | This assumes you have a 33 | [Heroku account](https://signup.heroku.com/dc), have installed the 34 | [Heroku toolbelt](https://toolbelt.heroku.com/), and have done a 35 | `heroku login` before. 36 | 37 | ``` sh 38 | git init 39 | git add -A 40 | git commit 41 | heroku create 42 | git push heroku master:master 43 | heroku open 44 | ``` 45 | 46 | ## Running with Foreman 47 | 48 | Heroku uses [Foreman](http://ddollar.github.io/foreman/) to run your 49 | app, which uses the `Procfile` in your repository to figure out which 50 | server command to run. Heroku also compiles and runs your code with a 51 | Leiningen "production" profile, instead of "dev". To locally simulate 52 | what Heroku does you can do: 53 | 54 | ``` sh 55 | lein with-profile -dev,+production uberjar && foreman start 56 | ``` 57 | 58 | Now your app is running at 59 | [http://localhost:5000](http://localhost:5000) in production mode. 60 | ## License 61 | 62 | Copyright © 2016 FIXME 63 | 64 | Distributed under the Eclipse Public License either version 1.0 or (at 65 | your option) any later version. 66 | 67 | ## Chestnut 68 | 69 | Created with [Chestnut](http://plexus.github.io/chestnut/) 0.14.0 (66af6f40). 70 | -------------------------------------------------------------------------------- /reagent/interact-with-the-repl.md: -------------------------------------------------------------------------------- 1 | # Interacting with the REPL 2 | 3 | With a running REPL you can make changes instantly. 4 | 5 | Place your browser where you can see both it and the editor / repl you are using to make changes. Then you can see just how fast Clojurescript development is. 6 | 7 | If all is well you now have a browser window saying 'Hello Chestnut', and a REPL prompt that looks like **cljs.user=>**. 8 | 9 | 10 | ## Make changes from the REPL 11 | 12 | Quickly experiment with your app by entering Clojurescript code into the repl. 13 | 14 | > #### Note::Use the REPL to change the message in the browser window 15 | 16 | > Enter the following lines of code in the REPL: 17 | 18 | ``` 19 | (in-ns conference-reagent.core) 20 | 21 | (swap! app-state assoc :text "I feel the power of the REPL") 22 | ``` 23 | 24 | The first line changes the namespace to `conference-reagent.core`, giving you access to the functions and definitions of our project. 25 | 26 | The second line updates a value in the application data model, `app-state`. Specifically this line runs the `assoc` function to update the value pointed to by `text` in the map that is referred to by the name `app-state`. The `swap` function is used as the map is defined as an `atom`, a mutable container, providing a managed way to update the state. 27 | 28 | > #### Hint:: 29 | 30 | > Atoms are covered in more detail in section ..., you dont need to understand them just yet. 31 | 32 | ## Make changes in the Clojurescript file 33 | 34 | Make more permanent changes by editing the file and saving the changes. 35 | 36 | > #### Note::Change files in the Project & watch the saved changes update the browser 37 | 38 | > Make the following changes to the CSS styles and main Clojurescript file, to see how quickly your changes are reloaded. 39 | 40 | 1. Open `resources/public/css/style.css` and change some styling of the H1 element. Notice how it's updated instantly in the browser. 41 | 42 | 2. Open `src/cljs/conference-reagent/core.cljs`, and change `dom/h1` to `dom/h2`. As soon as you save the file, your browser is updated. 43 | 44 | 3. Open `src/cljs/conference-reagent/core.cljs` and add the following code: 45 | ``` 46 | (swap! app-state assoc :text "Updates from the Editor") 47 | ``` 48 | Evaluate this line of code and again to see how the browser updates. 49 | -------------------------------------------------------------------------------- /create-a-project/coersing-types-and-java-lang.md: -------------------------------------------------------------------------------- 1 | # Theory: Specifying Types & java.lang 2 | 3 | Clojure has types that are created dynamically when the code is compiled, with everything being represented by Java objects as its compiled to Java bitecode. However you do not need to specify any specific types as Clojure simply infers the type and handles most type collision gracefully. The built in collections (list, map, vector & set) also support mixed types too. 4 | 5 | In our project we are using Jetty, a web application server written in Java. As its Java it needs a specific type of object passed as an argument for the port number, in this case an Integer. However, when we run the Clojure project, the argument we supply for the port number on the command line is treated as a String object. Therefore we need to change the port number from a Java String type to an Java Integer type. 6 | 7 | The `java.lang.` library is part of all Clojure projects, so as we are going to create a Java Integer it makes sense to simply use the `Integer` constructor with a String argument which returns a new Integer object. 8 | 9 | The `Integer. port-number` Clojure code is a short-hand for calling the `java.lang.Integer` constructor. The `.` is actually a macro in Clojure that provides a simple way to work with Java, allowing you to call Java objects as if they were Clojure functions. In Java you would have to use the form `Type instance-name = new Type(argument)`. In our example you would write this in Java as `String port = new String(port-number)` 10 | 11 | > **Hint** From the [Java 8 docs for Integer class](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html): 12 | `Integer(String s)` - constructs a newly allocated Integer object that represents the int value indicated by the String parameter. 13 | 14 | ## Theory: Its Java Objects underneath strings & numbers 15 | 16 | Strings and numbers are represented by Java objects underneath, so its convienient to use Java Classes to manipulate these simple data structures on the rare occasion you need a specific type. 17 | 18 | You can see the underlying Java types in Clojure using the `type` or `class` function. In the following example you can see the Java types for strings and numbers 19 | 20 | ![Clojure Types: String to Integer examples](/images/clojure-types-examples-string-to-integer.png) 21 | -------------------------------------------------------------------------------- /overview/data-types.md: -------------------------------------------------------------------------------- 1 | # Basic values 2 | 3 | Although you typically do not define types in Clojure (like you would do in Object Oriented languges) there are types underneath and Clojure decides on the most appropriate types to use for any given values. 4 | 5 | You can coerce a value to a particular type, typically when you are passing values to the host language (i.e Java / Javascript types) 6 | 7 | ## Numbers 8 | 9 | Integer and decimal numbers are supported 10 | 11 | 12 | ```clojure 13 | 1 2 3 4 5 6.0 14 | ``` 15 | 16 | ## Ratios 17 | 18 | From mathematics, a ratio is a relationship between two numbers indicating how many times the first number contains the second. In Clojure it also means that when dividing two or more integer numbers the value can be kept as a ratio if otherwise a decimal (real) number would be returned. 19 | 20 | ```clojure 21 | (/ 22 7.0) 22 | (/ 5 20) 23 | 24 | (/ (* 22/7 3) 3) 25 | ``` 26 | Ratios delay the need to drop into decimal numbers. Once you create a decimal number then everything it touches had a greater potential to becoming a decimal 27 | 28 | 29 | ## Strings 30 | 31 | Strings are defined using double quotes 32 | 33 | ```clojure 34 | "This is a string" 35 | "Strings are immutable too" 36 | ``` 37 | 38 | ## Symbols and binding 39 | 40 | The names we give functions and data structures to have an easy way to refer to them 41 | 42 | ```clojure 43 | (def name "value") 44 | (def name (fn [paramter] (behaviour))) 45 | 46 | (let [name "value"]) 47 | ``` 48 | 49 | ## Keywords 50 | 51 | Used for [enumerations](http://en.wikipedia.org/wiki/Enumeration) and as keys in maps (a map is a series of zero or more key value pairs) 52 | 53 | 54 | ## Collections 55 | 56 | There are four very special collections as part of the Clojure core, all immutable and referred to as [persistent data structures](persistent-data-structures.html) 57 | 58 | * List - a sequentially accessed linked list. The first element of a list is evaluated as a function call (unless a quote character is placed in front of the list) 59 | 60 | * Map - a key value pair collection, typically known as a hash maps 61 | 62 | * Vector - an array like structure in that it is indexed and fast for random access 63 | 64 | * Set - a unique set of elements, which can be ordered but are not by default 65 | 66 | See the section on [persistent data structures](persistent-data-structures.html) for more details. 67 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | /* @import url(https://fonts.googleapis.com/css?family=Raleway); */ 2 | @import url(https://fonts.googleapis.com/css?family=Ubuntu); 3 | @import url(https://fonts.googleapis.com/css?family=Ubuntu+Mono); 4 | 5 | /* Use Ubuntu font instead of the default Raleway font */ 6 | body { 7 | font-family: Ubuntu; 8 | font-size: 2rem; 9 | } 10 | 11 | /* Show keybindings to look like keys on a keyboard */ 12 | kbd { 13 | display: inline-block; 14 | border: 1px solid #ccc; 15 | border-radius: 4px; 16 | padding: 0.1em 0.5em; 17 | margin: 0 0.2em; 18 | box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #fff inset; 19 | color: #000000; 20 | background-color: #f7f7f7; 21 | } 22 | 23 | /* Over-ride maximum page width - default 800px */ 24 | /* - if over-ridden then paging arrows can appear over text */ 25 | 26 | .page-inner { 27 | max-width: 1000px; 28 | } 29 | 30 | 31 | .book-body h1,.book-body h2,.book-body h3,.book-body h4,.book-body h5,.book-body h6 { 32 | font-family : "Ubuntu", "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 33 | } 34 | 35 | .book-body p, .book-body div, .book-body span, .summary, .book-body li, .summary li a { 36 | font-family : "Ubuntu", "Open Sans", "Clear Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 37 | font-size: 2rem; 38 | } 39 | 40 | .book .book-body .page-wrapper .page-inner section.normal code { 41 | font-family: "Ubuntu Mono", Consolas, Menlo, Courier, monospace; 42 | } 43 | 44 | img, div, table { 45 | display: block; 46 | margin-left: auto; 47 | margin-right: auto; 48 | } 49 | 50 | /* Code blocks: boarder & increased font size from 85% to 108% */ 51 | .book .book-body .page-wrapper .page-inner section.normal .highlight pre, .book .book-body .page-wrapper .page-inner section.normal pre { 52 | background-color: #f7f7f7; 53 | border: 5px solid #D9EDF7; 54 | border-radius: 3px; 55 | font-size: 108%; 56 | line-height: 1.45; 57 | overflow: auto; 58 | padding: 16px; 59 | } 60 | 61 | /* Inline code: 62 | - highlight colour and background 63 | - increased font size to 108% */ 64 | .book .book-body .page-wrapper .page-inner section.normal code { 65 | background-color: #f7f7f7; 66 | color: #ba6ec5; /* Spacemacs purple*/ 67 | border-radius: 3px; 68 | font-size: 108%; 69 | margin: 0; 70 | padding: 0.2em 0; 71 | } 72 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-disqus/plugin.js: -------------------------------------------------------------------------------- 1 | require([ 2 | "gitbook", 3 | "jQuery" 4 | ], function(gitbook, $) { 5 | var use_identifier = false; 6 | 7 | function resetDisqus() { 8 | var $disqusDiv = $("
", { 9 | "id": "disqus_thread" 10 | }); 11 | $(".book-body .page-inner").append($disqusDiv); 12 | 13 | if (typeof DISQUS !== "undefined") { 14 | DISQUS.reset({ 15 | reload: true, 16 | config: function () { 17 | this.language = $('html').attr('lang') || "en"; 18 | this.page.url = window.location.href; 19 | 20 | if (use_identifier) { 21 | this.page.identifier = currentUrl(); 22 | } 23 | } 24 | }); 25 | } 26 | } 27 | 28 | function joinURL(baseUrl, url) { 29 | var theUrl = new URI(url); 30 | if (theUrl.is("relative")) { 31 | theUrl = theUrl.absoluteTo(baseUrl); 32 | } 33 | return theUrl.toString(); 34 | } 35 | 36 | function currentUrl() { 37 | var location = new URI(window.location.href), 38 | base = joinURL(window.location.href, gitbook.state.basePath), 39 | current = location.relativeTo(base).toString(), 40 | language = $('html').attr('lang'), 41 | parent = joinURL(base, '..'), 42 | folder = new URI(base).relativeTo(parent).toString(); 43 | 44 | // If parent folder is the same as language, we assume translated books 45 | if (folder.replace(/\/$/, "") === language) { 46 | current = folder + current; 47 | } 48 | 49 | return current; 50 | } 51 | 52 | gitbook.events.bind("start", function(e, config) { 53 | config.disqus = config.disqus || {}; 54 | var disqus_shortname = config.disqus.shortName; 55 | var disqus_config = function() { 56 | this.language = $('html').attr('lang') || "en"; 57 | }; 58 | 59 | if (config.disqus.useIdentifier) { 60 | use_identifier = true; 61 | var disqus_identifier = currentUrl(); 62 | } 63 | 64 | /* * * DON'T EDIT BELOW THIS LINE * * */ 65 | (function() { 66 | var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true; 67 | dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js'; 68 | (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq); 69 | })(); 70 | 71 | resetDisqus(); 72 | }); 73 | 74 | gitbook.events.bind("page.change", resetDisqus); 75 | }); 76 | -------------------------------------------------------------------------------- /clojure-syntax/namespace.md: -------------------------------------------------------------------------------- 1 | # Namespace 2 | 3 | A namespace in Clojure is used to manage the logical seperation of code, usually along features of the application, service or library you are developing. A namespace contains data structures and functions, limiting their scope to that function. 4 | 5 | Unless otherwise defined, any function can call any other function in the namespace by just the function name. The same is true for any naed data structures, they can be called from anywhere in the namespace with just their name. 6 | 7 | > **Hint** Remember that Clojure is evaluated from top to bottom, so if you are calling a named function or data structure, it must have had its definition evaluated first. 8 | 9 | ## Including another namespace 10 | 11 | `require` or `:require` is used to enable access to the functions & named datastructures in another namespace than the current one. 12 | 13 | ```clojure 14 | (ns my-namespace.core 15 | :require [clojure.java.io]) 16 | 17 | (defn read-the-file [filename] 18 | (line-seq (clojure.java.io/reader filename))) 19 | 20 | (read-the-file "project.clj") 21 | ``` 22 | 23 | The `reader` funciton can be accessed, however, we still need to include the fully qualified namespace path, `clojure.java.io`. 24 | 25 | If the namespace has a long name, you can provide an alias (please use names that keep the code readable) 26 | 27 | ```clojure 28 | (ns my-namespace.core 29 | :require [clojure.java.io :as java-io]) 30 | 31 | (defn read-the-file [filename] 32 | (line-seq (java-io/reader filename))) 33 | 34 | (read-the-file "project.clj") 35 | ``` 36 | 37 | Or if you are going to use the function multiple times in the current namespace, you could also include that function directly, no longer requiring any kind of namespace qualifier 38 | 39 | ```clojure 40 | (ns my-namespace.core 41 | :require [clojure.java.io :as [reader]]) 42 | 43 | (defn read-the-file [filename] 44 | (line-seq (reader filename))) 45 | 46 | (read-the-file "project.clj") 47 | ``` 48 | 49 | > **Hint** You may see `use` or `:use` as an alternative approach. While this will work, it also includes everything from the other namespace into your current one. This is seen as a bad practice, especially when writing libraries, as you can end up including a great many unused functions into the namespace. 50 | 51 | > As Clojure is typically composed of many libraries, its prudent to only include the specific things you need from another namespace. This also helps reduce conflicts when including multiple libraries in your project. 52 | 53 | 54 | ## External libraries 55 | 56 | To use a library that is not part of your project you also need to include it as a dependency. You can do this using the `:dependencis` section of the Leiningen project file. 57 | 58 | ```clojure 59 | ;; include and example dependency - eg ring, compujure 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /install/cider.md: -------------------------------------------------------------------------------- 1 | # CIDER 2 | 3 | You can use CIDER, the Emacs client for Clojure, with Clojurescript projects. 4 | 5 | In the current version of CIDER you can have both a Clojure and ClojureScript REPL active at the same time, so forms evaluated from a CLJ or CLJS file automatically go to the right place. 6 | 7 | Run the command `cider-jack-in-clojurescript` to start two nREPL sessions. 8 | 9 | > For a more detailed background to how this works, see the LambdaIsland article ["Using Figwheel With Emacs, part 2: CIDER"](https://lambdaisland.com/episodes/figwheel-emacs-cider) (account required for access) 10 | 11 | ## Clojurescript environments 12 | 13 | You can choose from one of several REPL's to use with CIDER. By default CIDER is configured to use the Java Rhino REPL, however most people will use Figwheel instead. 14 | 15 | CIDER use Rhino, the JavaScript engine bundled with Java. Rhino is useful if you want to use Java classes straight from ClojureScript. 16 | 17 | Figwheel is the most common approach for Clojurescript websites. 18 | 19 | 20 | ## Setting the environment 21 | 22 | CIDER can be configured by customizing the `cider-cljs-lein-repl` variable. 23 | 24 | This variable is set to `(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))`so we need to change it for other environments. CIDER makes this easy by providing a list to choose from. 25 | 26 | Lets change the setting to use Figwheel: 27 | 28 | 1. Open the customize interface with `M-x customize-variable cider-cljs-lein-repl`. 29 | 30 | 2. Tab your way to the button that reads “Value Menu” and press Enter. 31 | 32 | 3. A buffer is displayed with a list of choices, the second of which is “Figwheel sidecar”. Press 1, and then press the "Apply and Save" button. 33 | 34 | Now if you look at `cider-cljs-lein-repl` you’ll see it’s switched to a Figwheel REPL. 35 | 36 | Use Directory Local Variables to change this on a per project basis. 37 | 38 | ```clojure 39 | (do 40 | (require 'figwheel-sidecar.repl-api) 41 | (figwheel-sidecar.repl-api/start-figwheel!) 42 | (figwheel-sidecar.repl-api/cljs-repl)) 43 | ``` 44 | 45 | A more elaborate snippet that tries to detect the type of project and run the appropriate environment. 46 | 47 | ```clojure 48 | (setq cider-cljs-lein-repl 49 | "(cond 50 | (and (resolve 'user/run) (resolve 'user/browser-repl)) ;; Chestnut projects 51 | (eval '(do (user/run) 52 | (user/browser-repl))) 53 | 54 | (try 55 | (require 'figwheel-sidecar.repl-api) 56 | (resolve 'figwheel-sidecar.repl-api/start-figwheel!) 57 | (catch Throwable _)) 58 | (eval '(do (figwheel-sidecar.repl-api/start-figwheel!) 59 | (figwheel-sidecar.repl-api/cljs-repl))) 60 | 61 | (try 62 | (require 'cemerick.piggieback) 63 | (resolve 'cemerick.piggieback/cljs-repl) 64 | (catch Throwable _)) 65 | (eval '(cemerick.piggieback/cljs-repl (cljs.repl.rhino/repl-env))) 66 | 67 | :else 68 | (throw (ex-info \"Failed to initialize CLJS repl. Add com.cemerick/piggieback and optionally figwheel-sidecar to your project.\" {})))") 69 | ``` 70 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-sharing/buttons.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | var SITES = { 3 | 'facebook': { 4 | 'label': 'Facebook', 5 | 'icon': 'fa fa-facebook', 6 | 'onClick': function(e) { 7 | e.preventDefault(); 8 | window.open('http://www.facebook.com/sharer/sharer.php?s=100&p[url]='+encodeURIComponent(location.href)); 9 | } 10 | }, 11 | 'twitter': { 12 | 'label': 'Twitter', 13 | 'icon': 'fa fa-twitter', 14 | 'onClick': function(e) { 15 | e.preventDefault(); 16 | window.open('http://twitter.com/home?status='+encodeURIComponent(document.title+' '+location.href)); 17 | } 18 | }, 19 | 'google': { 20 | 'label': 'Google+', 21 | 'icon': 'fa fa-google-plus', 22 | 'onClick': function(e) { 23 | e.preventDefault(); 24 | window.open('https://plus.google.com/share?url='+encodeURIComponent(location.href)); 25 | } 26 | }, 27 | 'weibo': { 28 | 'label': 'Weibo', 29 | 'icon': 'fa fa-weibo', 30 | 'onClick': function(e) { 31 | e.preventDefault(); 32 | window.open('http://service.weibo.com/share/share.php?content=utf-8&url='+encodeURIComponent(location.href)+'&title='+encodeURIComponent(document.title)); 33 | } 34 | }, 35 | 'instapaper': { 36 | 'label': 'Instapaper', 37 | 'icon': 'fa fa-instapaper', 38 | 'onClick': function(e) { 39 | e.preventDefault(); 40 | window.open('http://www.instapaper.com/text?u='+encodeURIComponent(location.href)); 41 | } 42 | }, 43 | 'vk': { 44 | 'label': 'VK', 45 | 'icon': 'fa fa-vk', 46 | 'onClick': function(e) { 47 | e.preventDefault(); 48 | window.open('http://vkontakte.ru/share.php?url='+encodeURIComponent(location.href)); 49 | } 50 | } 51 | }; 52 | 53 | 54 | 55 | gitbook.events.bind('start', function(e, config) { 56 | var opts = config.sharing; 57 | 58 | // Create dropdown menu 59 | var menu = $.map(opts.all, function(id) { 60 | var site = SITES[id]; 61 | 62 | return { 63 | text: site.label, 64 | onClick: site.onClick 65 | }; 66 | }); 67 | 68 | // Create main button with dropdown 69 | if (menu.length > 0) { 70 | gitbook.toolbar.createButton({ 71 | icon: 'fa fa-share-alt', 72 | label: 'Share', 73 | position: 'right', 74 | dropdown: [menu] 75 | }); 76 | } 77 | 78 | // Direct actions to share 79 | $.each(SITES, function(sideId, site) { 80 | if (!opts[sideId]) return; 81 | 82 | gitbook.toolbar.createButton({ 83 | icon: site.icon, 84 | label: site.text, 85 | position: 'right', 86 | onClick: site.onClick 87 | }); 88 | }); 89 | }); 90 | }); 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Practicalli ClojureScript 2 | Content for the [Practicalli ClojureScript book](https://practicalli.github.io/clojurescript) 3 | 4 | ![Practicalli ClojureScript](/images/practicalli-clojurescript-book-banner.png) 5 | 6 | 7 | > #### Warning:: Requires major update 8 | > Requires a major update to catch up with changes in Clojure and ClojureScript, especially around shadown-cljs which is not yet covered. 9 | > 10 | > An example that is still relevant is the [TicTacToe game with ClojureScript, Reagent and Scalable Vector Graphics (SVG)](/reagent-projects/tic-tac-toe/index.html) 11 | 12 | This workshop is aimed at those new to writing client-side web applications using the ClojureScript programming language, a functional programming language based on Lisp and compiled to JavaScript. 13 | 14 | ClojureScript and client side development are covered in a highly practical way, so you can evaluate where ClojureScript is useful in your own projects. 15 | 16 | This workshop guides you through the basics of client-side web development and functional programming in ClojureScript. Design approaches common to ClojureScript & React will be covered along the way. 17 | 18 | ## Creative commons license 19 |

Practicalli Clojure by Practicalli Creative Commons Attribution Share-Alike 4.0 International

20 | 21 | 22 | 23 | ![Clojure logo](images/clojurescript-react.png) 24 | 25 | > #### Warning::Requires major development and will undergo a re-write in the later part of 2018 26 | 27 | 28 | This workshop is aimed at those new to writing client-side web applications using the ClojureScript programming language, a functional programming language based on Lisp and compiled to JavaScript. 29 | 30 | ClojureScript and client side development are covered in a highly practical way, so you can evaluate where ClojureScript is useful in your own projects. 31 | 32 | This workshop guides you through the basics of client-side web development and functional programming in ClojureScript. Design approaches common to ClojureScript & React will be covered along the way. 33 | 34 | > #### Hint:: Please help the development of this workshop by leaving comments on anything that does not work, does not make sense or any ideas and topics that you think are missing. 35 | 36 | > The contents of this book has been written with markdown and is available via a [Github repository](https://github.com/practicalli/clojurescript-content) and of course pull requests are welcome. 37 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-highlight/ebook.css: -------------------------------------------------------------------------------- 1 | pre, 2 | code { 3 | /* http://jmblog.github.io/color-themes-for-highlightjs */ 4 | /* Tomorrow Comment */ 5 | /* Tomorrow Red */ 6 | /* Tomorrow Orange */ 7 | /* Tomorrow Yellow */ 8 | /* Tomorrow Green */ 9 | /* Tomorrow Aqua */ 10 | /* Tomorrow Blue */ 11 | /* Tomorrow Purple */ 12 | } 13 | pre .hljs-comment, 14 | code .hljs-comment, 15 | pre .hljs-title, 16 | code .hljs-title { 17 | color: #8e908c; 18 | } 19 | pre .hljs-variable, 20 | code .hljs-variable, 21 | pre .hljs-attribute, 22 | code .hljs-attribute, 23 | pre .hljs-tag, 24 | code .hljs-tag, 25 | pre .hljs-regexp, 26 | code .hljs-regexp, 27 | pre .hljs-deletion, 28 | code .hljs-deletion, 29 | pre .ruby .hljs-constant, 30 | code .ruby .hljs-constant, 31 | pre .xml .hljs-tag .hljs-title, 32 | code .xml .hljs-tag .hljs-title, 33 | pre .xml .hljs-pi, 34 | code .xml .hljs-pi, 35 | pre .xml .hljs-doctype, 36 | code .xml .hljs-doctype, 37 | pre .html .hljs-doctype, 38 | code .html .hljs-doctype, 39 | pre .css .hljs-id, 40 | code .css .hljs-id, 41 | pre .css .hljs-class, 42 | code .css .hljs-class, 43 | pre .css .hljs-pseudo, 44 | code .css .hljs-pseudo { 45 | color: #c82829; 46 | } 47 | pre .hljs-number, 48 | code .hljs-number, 49 | pre .hljs-preprocessor, 50 | code .hljs-preprocessor, 51 | pre .hljs-pragma, 52 | code .hljs-pragma, 53 | pre .hljs-built_in, 54 | code .hljs-built_in, 55 | pre .hljs-literal, 56 | code .hljs-literal, 57 | pre .hljs-params, 58 | code .hljs-params, 59 | pre .hljs-constant, 60 | code .hljs-constant { 61 | color: #f5871f; 62 | } 63 | pre .ruby .hljs-class .hljs-title, 64 | code .ruby .hljs-class .hljs-title, 65 | pre .css .hljs-rules .hljs-attribute, 66 | code .css .hljs-rules .hljs-attribute { 67 | color: #eab700; 68 | } 69 | pre .hljs-string, 70 | code .hljs-string, 71 | pre .hljs-value, 72 | code .hljs-value, 73 | pre .hljs-inheritance, 74 | code .hljs-inheritance, 75 | pre .hljs-header, 76 | code .hljs-header, 77 | pre .hljs-addition, 78 | code .hljs-addition, 79 | pre .ruby .hljs-symbol, 80 | code .ruby .hljs-symbol, 81 | pre .xml .hljs-cdata, 82 | code .xml .hljs-cdata { 83 | color: #718c00; 84 | } 85 | pre .css .hljs-hexcolor, 86 | code .css .hljs-hexcolor { 87 | color: #3e999f; 88 | } 89 | pre .hljs-function, 90 | code .hljs-function, 91 | pre .python .hljs-decorator, 92 | code .python .hljs-decorator, 93 | pre .python .hljs-title, 94 | code .python .hljs-title, 95 | pre .ruby .hljs-function .hljs-title, 96 | code .ruby .hljs-function .hljs-title, 97 | pre .ruby .hljs-title .hljs-keyword, 98 | code .ruby .hljs-title .hljs-keyword, 99 | pre .perl .hljs-sub, 100 | code .perl .hljs-sub, 101 | pre .javascript .hljs-title, 102 | code .javascript .hljs-title, 103 | pre .coffeescript .hljs-title, 104 | code .coffeescript .hljs-title { 105 | color: #4271ae; 106 | } 107 | pre .hljs-keyword, 108 | code .hljs-keyword, 109 | pre .javascript .hljs-function, 110 | code .javascript .hljs-function { 111 | color: #8959a8; 112 | } 113 | pre .hljs, 114 | code .hljs { 115 | display: block; 116 | background: white; 117 | color: #4d4d4c; 118 | padding: 0.5em; 119 | } 120 | pre .coffeescript .javascript, 121 | code .coffeescript .javascript, 122 | pre .javascript .xml, 123 | code .javascript .xml, 124 | pre .tex .hljs-formula, 125 | code .tex .hljs-formula, 126 | pre .xml .javascript, 127 | code .xml .javascript, 128 | pre .xml .vbscript, 129 | code .xml .vbscript, 130 | pre .xml .css, 131 | code .xml .css, 132 | pre .xml .hljs-cdata, 133 | code .xml .hljs-cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /create-a-project/add-a-webserver-function.md: -------------------------------------------------------------------------------- 1 | # Add a webserver 2 | 3 | Use the Ring Library to create the simplest possible web server, one that will simply return a message regardless of the web page we ask for. 4 | 5 | ![Ring - Adaptor and anonymous function](../images/clojure-ring-adaptor-anonymous-function.png) 6 | 7 | ## Including Ring in the namespace 8 | 9 | First make the ring library functions accessible in the namespace. Using `:require` we can add one or more libraries to our namespace. We can also use the `:as` keyword to specify a short-hand way of refering to a library. 10 | 11 | > **Note** Delete all the code in `src/todo_list/core.clj` and replace it with the following code, adding the ring adaptor for Jetty to our project. 12 | 13 | ```clojure 14 | (ns todo-list.core 15 | (:require [ring.adapter.jetty :as jetty])) 16 | ``` 17 | 18 | This expression defines the current namespace as `todo-list.core`, providing a scope for all the functions and data structures we define within it. 19 | 20 | The `:require` expression makes the `ring.adaptor.jetty` namespace accessible within the `todo-list.core` namespace. We can now call any of the public functions in the `ring.adaptor.jetty` namespace. We defined an alias called `jetty` so to call the `run-jetty` function we use `jetty/run-jetty` rather than the fully qualified namespace of `ring.adaptor.jetty/run-jetty` 21 | 22 | You can specify any valid Clojure name for a namespace alias, however please consider the readability of your code and choose a meaningful alias. Later in the workshop we will show other options for including functions from other namespaces. 23 | 24 | ## Add a main function to run Jetty 25 | 26 | > **Note** Add a function called `-main` to the `src/todo_list/core.clj` file. 27 | 28 | > The `-main` function takes a port number as an argument which we pass when running the application. The Ring Jetty adaptor is used to run an instance of Jetty. The `-main` function contains an anonymous function (lambda) that takes any request and returns a _response map_. 29 | 30 | A response map contains the following key / value pairs 31 | * `:status` - the result of the request, eg. 200 OK, 401 Not Found, etc 32 | * `:body` - the content to be returned (web page, json, etc) 33 | * `:headers` - a map of standard headers included in any web browser response 34 | 35 | ```clojure 36 | (defn -main 37 | "A very simple web server using Ring & Jetty" 38 | [port-number] 39 | (jetty/run-jetty 40 | (fn [request] {:status 200 41 | :body "

Hello, Clojure World

Welcome to your first Clojure app. This message is returned regardless of the request, sorry

" 42 | :headers {}}) 43 | {:port (Integer. port-number)})) 44 | ``` 45 | 46 | > **Hint** Using a `-` at the start of the `-main` function is a naming convention, helping you see which function is the entry point to your program. Leiningen also looks for this -main function by default when running your application. 47 | 48 | > The `Integer.` function is a call to `java.lang.Integer`. The `.` is a special form that tells Clojure to treat this name as a call to Java. See [coersing types and java.lang](coersing-types-and-java-lang.html) 49 | 50 | > The `jetty/run-jetty` function takes two arguments. In our example, the first argument is an anonymous function that returns a map (the response to the browser request); the second argument is a port number to run the jetty server on expressed as a Java Integer object. 51 | 52 |
53 | 54 | ## The complete code for the web server 55 | 56 | The code in `src/todo_list/core.clj` should now look like this: 57 | 58 | ```clojure 59 | (ns todo-list.core 60 | (:require [ring.adapter.jetty :as jetty])) 61 | 62 | (defn -main 63 | "A very simple web server using Ring & Jetty" 64 | [port-number] 65 | (jetty/run-jetty 66 | (fn [request] {:status 200 67 | :body "

Hello, Clojure World

Welcome to your first Clojure app. This message is returned regardless of the request, sorry

" 68 | :headers {}}) 69 | {:port (Integer. port-number)})) 70 | ``` 71 | 72 | -------------------------------------------------------------------------------- /images/reactive-traits.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 12 | 14 | Responsive 15 | 16 | 18 | 20 | 22 | Resilient 23 | 24 | 25 | 27 | 29 | 31 | 32 | Message Driven 33 | 34 | 36 | 38 | 40 | Elastic 41 | 42 | 43 | 44 | 45 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-callouts/plugin.css: -------------------------------------------------------------------------------- 1 | .panel { 2 | margin-bottom: 20px; 3 | background-color: #ffffff; 4 | border: 1px solid transparent; 5 | border-radius: 4px; 6 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 7 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 8 | } 9 | 10 | .panel-body { 11 | padding: 15px; 12 | } 13 | 14 | .panel-heading { 15 | padding: 10px 15px; 16 | border-bottom: 1px solid transparent; 17 | border-top-right-radius: 3px; 18 | border-top-left-radius: 3px; 19 | } 20 | 21 | .panel-title { 22 | margin-top: 0 !important; 23 | margin-bottom: 0 !important; 24 | font-size: 16px !important; 25 | color: inherit; 26 | } 27 | 28 | .panel-title > a, 29 | .panel-title > small, 30 | .panel-title > .small, 31 | .panel-title > small > a, 32 | .panel-title > .small > a { 33 | color: inherit; 34 | } 35 | 36 | .panel-default { 37 | border-color: #dddddd; 38 | } 39 | .panel-default > .panel-heading { 40 | color: #333333; 41 | background-color: #f5f5f5; 42 | border-color: #dddddd; 43 | } 44 | 45 | .panel-primary { 46 | border-color: #337ab7; 47 | background-color: #D8EDF8; 48 | } 49 | 50 | .panel-primary > .panel-heading { 51 | color: #ffffff; 52 | background-color: #337ab7; 53 | border-color: #337ab7; 54 | } 55 | 56 | .panel-success { 57 | border-color: #DFF0D7; 58 | background-color: #DFF0D7; 59 | } 60 | .panel-success > .panel-heading { 61 | color: #ffffff; 62 | background-color: #58B957; 63 | border-color: #DFF0D7; 64 | } 65 | 66 | .panel-info { 67 | border-color: #E7F2FA; 68 | background-color: #E7F2FA; 69 | } 70 | .panel-info > .panel-heading { 71 | background-color: #67AFE0; 72 | color: #ffffff; 73 | border-color: #E7F2FA; 74 | } 75 | 76 | .panel-warning { 77 | border-color: #FFEDCA; 78 | background-color: #FFEDCA; 79 | } 80 | .panel-warning > .panel-heading { 81 | color: #ffffff; 82 | background-color: #F1B37A; 83 | border-color: #FFEDCA; 84 | } 85 | 86 | .panel-danger { 87 | border-color: #F2DEDE; 88 | background-color:#F2DEDE; 89 | } 90 | .panel-danger > .panel-heading { 91 | color: #ffffff; 92 | background-color: #DB524B; 93 | border-color: #F2DEDE; 94 | } 95 | 96 | // Night Theme 97 | 98 | .book.color-theme-2 .panel { 99 | margin-bottom: 20px; 100 | background-color: #1c1f2b; 101 | border: 1px solid transparent; 102 | border-radius: 4px; 103 | -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 104 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); 105 | } 106 | 107 | .book.color-theme-2 .panel-body { 108 | padding: 15px; 109 | } 110 | 111 | .book.color-theme-2 .panel-heading { 112 | padding: 10px 15px; 113 | border-bottom: 1px solid transparent; 114 | border-top-right-radius: 3px; 115 | border-top-left-radius: 3px; 116 | background-color: #1c1f2b; 117 | } 118 | 119 | .book.color-theme-2 .panel-title { 120 | margin-top: 0 !important; 121 | margin-bottom: 0 !important; 122 | font-size: 16px !important; 123 | color: inherit; 124 | } 125 | 126 | .book.color-theme-2 .panel-title > a, 127 | .book.color-theme-2 .panel-title > small, 128 | .book.color-theme-2 .panel-title > .small, 129 | .book.color-theme-2 .panel-title > small > a, 130 | .book.color-theme-2 .panel-title > .small > a { 131 | color: inherit; 132 | } 133 | 134 | .book.color-theme-2 .panel-default { 135 | border-color: #dddddd; 136 | } 137 | .book.color-theme-2 .panel-default > .panel-heading { 138 | color: #333333; 139 | background-color: #f5f5f5; 140 | border-color: #dddddd; 141 | } 142 | 143 | .book.color-theme-2 .panel-primary { 144 | border-color: #245784; 145 | background-color: #3F4346; 146 | } 147 | 148 | .book.color-theme-2 .panel-primary > .panel-heading { 149 | color: #ffffff; 150 | background-color: #245784; 151 | border-color: #245784; 152 | } 153 | 154 | .book.color-theme-2 .panel-success { 155 | border-color: #2A582A; 156 | background-color: #3B3E3A; 157 | } 158 | .book.color-theme-2 .panel-success > .panel-heading { 159 | color: #ffffff; 160 | background-color: #2A582A; 161 | border-color: #2A582A; 162 | } 163 | 164 | .book.color-theme-2 .panel-info { 165 | border-color: #3F6A88; 166 | background-color: #3A3C3E; 167 | } 168 | .book.color-theme-2 .panel-info > .panel-heading { 169 | background-color: #3F6A88; 170 | color: #ffffff; 171 | border-color: #3F6A88; 172 | } 173 | 174 | .book.color-theme-2 .panel-warning { 175 | border-color: #AF845E; 176 | background-color: #524D42; 177 | } 178 | .book.color-theme-2 .panel-warning > .panel-heading { 179 | color: #ffffff; 180 | background-color: #AF845E; 181 | border-color: #AF845E; 182 | } 183 | 184 | .book.color-theme-2 .panel-danger { 185 | border-color: #751712; 186 | background-color:#3A3434; 187 | } 188 | .book.color-theme-2 .panel-danger > .panel-heading { 189 | color: #ffffff; 190 | background-color: #751712; 191 | border-color: #751712; 192 | } 193 | -------------------------------------------------------------------------------- /install/chestnut.md: -------------------------------------------------------------------------------- 1 | # Chestnut 2 | 3 | ![Chestnut](/images/chestnut.png) 4 | 5 | [Chestnut](https://github.com/plexus/chestnut) is a Leiningen template for a ClojureScript app based on Om, featuring a great dev setup, and easy deployment. 6 | 7 | For smooth development you get instant reloading of Clojure, ClojureScript, and CSS. A browser-connected REPL is also included. 8 | 9 | For deployment you get uberjar support, meaning you can get all your code compiled, optimized, and packaged in a single executable JAR file. It also contains the necessary artifacts to work on Heroku out of the box. 10 | 11 | Need help? Ask on the [mailing list](http://chestnut.aren.io/) 12 | 13 | 14 | ## Chestnut 0.9.0-SNAPSHOT 15 | 16 | A major update is in the works. To try it out, use `lein new chestnut my-project --snapshot`. 17 | 18 | * Use Figwheel for REPL and Ring server 19 | * Remove boilerplate that's no longer needed 20 | * Drop Enlive and simplify the dev setup 21 | * Drop CLJX support 22 | * Better inline documentation 23 | 24 | ## Usage 25 | 26 | ``` 27 | lein new chestnut 28 | ``` 29 | 30 | After that open the README of your generated project for detailed 31 | instructions. 32 | 33 | ## Command line 34 | 35 | ``` 36 | $ lein repl 37 | 38 | (run) 39 | (browser-repl) 40 | ``` 41 | 42 | Wait a bit, then browse to 43 | [http://localhost:10555](http://localhost:10555). 44 | 45 | ### Lighttable 46 | 47 | Lighttable provides a tighter integration for live coding with an inline browser-tab. Rather than evaluating cljs on the command line with weasel repl, evaluate code and preview pages inside Lighttable. 48 | 49 | Steps: After running `(run)`, open a browser tab in Lighttable. Open a cljs file from within a project, go to the end of an s-expression and hit Cmd-ENT. Lighttable will ask you which client to connect. Click 'Connect a client' and select 'Browser'. Browse to [http://localhost:10555](http://localhost:10555) 50 | 51 | View LT's console to see a Chrome js console. 52 | 53 | Hereafter, you can save a file and see changes or evaluate cljs code (without saving a file). Note that running a weasel server is not required to evaluate code in Lighttable. 54 | 55 | ### Emacs/Cider 56 | 57 | Start a repl in the context of your project with `M-x cider-jack-in`. 58 | 59 | Switch to repl-buffer with `C-c C-z` and start web and figwheel servers with `(run)`, and weasel server with `(browser-repl`). Load [http://localhost:10555](http://localhost:10555) on an external browser, which connects to weasel, and start evaluating cljs inside Cider. 60 | 61 | ## List of Contents 62 | 63 | This template gives you everything you need to start developing 64 | Clojure/ClojureScript apps effectively. It comes with 65 | 66 | * [Figwheel](https://github.com/bhauman/lein-figwheel) Automatically 67 | reload your ClojureScript and CSS as soon as you save the file, no 68 | need for browser refresh. 69 | * [Om](https://github.com/swannodette/om) ClojureScript interface to 70 | Facebook's React. 71 | * [Ring](https://github.com/ring-clojure/ring) Clojure's de facto HTTP 72 | interface. Chestnut uses a Jetty or HttpKit server to serve the 73 | Clojurescript app. This way you already have an HTTP server running 74 | in case you want to add server-side functionality. Chestnut also 75 | inserts a Ring middleware to reload server-side Clojure files. 76 | * Heroku support. Chestnut apps have all the bits and pieces to be 77 | deployable to Heroku. Getting your app on the web is as simple as 78 | `git push`. 79 | * Unit tests for both Clojure and CLJS. Or you can decide to use Speclj instead, both for CLJ and CLJS. 80 | Both specs and CLJS tests can be run in "auto" mode. 81 | 82 | ## Options 83 | 84 | * `--http-kit` Use [HTTP Kit](http://http-kit.org/server.html) instead 85 | of Jetty 86 | * `--site-middleware` Use the `ring.middleware.defaults.site-defaults` middleware 87 | (session, CSRF), instead of `ring.middleware.defaults.api-defaults` (see 88 | [ring.defaults documentation](https://github.com/ring-clojure/ring-defaults)) 89 | * `--om-tools` Use Prismatic's 90 | [om-tools.dom](https://github.com/Prismatic/om-tools) instead of 91 | `om.dom` 92 | * `--less` Use [less](https://github.com/montoux/lein-less) for 93 | compiling Less CSS files. 94 | * `--speclj` Use [speclj](http://speclj.com) test runner for clj and 95 | cljs, and disable the core cljs.test tests. 96 | 97 | Use `--` to separate these options from Leiningen's options, 98 | e.g. `lein new chestnut foo -- --om-tools --http-kit` 99 | 100 | ## Local copy 101 | 102 | If you want to customize Chestnut, or try unreleased features, you can 103 | run directly from master like this: 104 | 105 | ``` sh 106 | git clone https://github.com/plexus/chestnut.git 107 | cd chestnut 108 | lein install 109 | ``` 110 | 111 | Note that master may be partially or wholly broken. I try to do 112 | extensive manual testing before releasing a new stable version, so if 113 | you don't like surprises then stick to the version on Clojars. Issue 114 | reports and pull requests are very welcome. 115 | 116 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-search/search.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'gitbook', 3 | 'jquery' 4 | ], function(gitbook, $) { 5 | var MAX_RESULTS = 15; 6 | var MAX_DESCRIPTION_SIZE = 500; 7 | 8 | var usePushState = (typeof history.pushState !== 'undefined'); 9 | 10 | // DOM Elements 11 | var $body = $('body'); 12 | var $bookSearchResults; 13 | var $searchInput; 14 | var $searchList; 15 | var $searchTitle; 16 | var $searchResultsCount; 17 | var $searchQuery; 18 | 19 | // Throttle search 20 | function throttle(fn, wait) { 21 | var timeout; 22 | 23 | return function() { 24 | var ctx = this, args = arguments; 25 | if (!timeout) { 26 | timeout = setTimeout(function() { 27 | timeout = null; 28 | fn.apply(ctx, args); 29 | }, wait); 30 | } 31 | }; 32 | } 33 | 34 | function displayResults(res) { 35 | $bookSearchResults.addClass('open'); 36 | 37 | var noResults = res.count == 0; 38 | $bookSearchResults.toggleClass('no-results', noResults); 39 | 40 | // Clear old results 41 | $searchList.empty(); 42 | 43 | // Display title for research 44 | $searchResultsCount.text(res.count); 45 | $searchQuery.text(res.query); 46 | 47 | // Create an

  • element for each result 48 | res.results.forEach(function(res) { 49 | var $li = $('
  • ', { 50 | 'class': 'search-results-item' 51 | }); 52 | 53 | var $title = $('

    '); 54 | 55 | var $link = $('', { 56 | 'href': gitbook.state.basePath + '/' + res.url, 57 | 'text': res.title 58 | }); 59 | 60 | var content = res.body.trim(); 61 | if (content.length > MAX_DESCRIPTION_SIZE) { 62 | content = content.slice(0, MAX_DESCRIPTION_SIZE).trim()+'...'; 63 | } 64 | var $content = $('

    ').html(content); 65 | 66 | $link.appendTo($title); 67 | $title.appendTo($li); 68 | $content.appendTo($li); 69 | $li.appendTo($searchList); 70 | }); 71 | } 72 | 73 | function launchSearch(q) { 74 | // Add class for loading 75 | $body.addClass('with-search'); 76 | $body.addClass('search-loading'); 77 | 78 | // Launch search query 79 | throttle(gitbook.search.query(q, 0, MAX_RESULTS) 80 | .then(function(results) { 81 | displayResults(results); 82 | }) 83 | .always(function() { 84 | $body.removeClass('search-loading'); 85 | }), 1000); 86 | } 87 | 88 | function closeSearch() { 89 | $body.removeClass('with-search'); 90 | $bookSearchResults.removeClass('open'); 91 | } 92 | 93 | function launchSearchFromQueryString() { 94 | var q = getParameterByName('q'); 95 | if (q && q.length > 0) { 96 | // Update search input 97 | $searchInput.val(q); 98 | 99 | // Launch search 100 | launchSearch(q); 101 | } 102 | } 103 | 104 | function bindSearch() { 105 | // Bind DOM 106 | $searchInput = $('#book-search-input input'); 107 | $bookSearchResults = $('#book-search-results'); 108 | $searchList = $bookSearchResults.find('.search-results-list'); 109 | $searchTitle = $bookSearchResults.find('.search-results-title'); 110 | $searchResultsCount = $searchTitle.find('.search-results-count'); 111 | $searchQuery = $searchTitle.find('.search-query'); 112 | 113 | // Launch query based on input content 114 | function handleUpdate() { 115 | var q = $searchInput.val(); 116 | 117 | if (q.length == 0) { 118 | closeSearch(); 119 | } 120 | else { 121 | launchSearch(q); 122 | } 123 | } 124 | 125 | // Detect true content change in search input 126 | // Workaround for IE < 9 127 | var propertyChangeUnbound = false; 128 | $searchInput.on('propertychange', function(e) { 129 | if (e.originalEvent.propertyName == 'value') { 130 | handleUpdate(); 131 | } 132 | }); 133 | 134 | // HTML5 (IE9 & others) 135 | $searchInput.on('input', function(e) { 136 | // Unbind propertychange event for IE9+ 137 | if (!propertyChangeUnbound) { 138 | $(this).unbind('propertychange'); 139 | propertyChangeUnbound = true; 140 | } 141 | 142 | handleUpdate(); 143 | }); 144 | 145 | // Push to history on blur 146 | $searchInput.on('blur', function(e) { 147 | // Update history state 148 | if (usePushState) { 149 | var uri = updateQueryString('q', $(this).val()); 150 | history.pushState({ path: uri }, null, uri); 151 | } 152 | }); 153 | } 154 | 155 | gitbook.events.on('page.change', function() { 156 | bindSearch(); 157 | closeSearch(); 158 | 159 | // Launch search based on query parameter 160 | if (gitbook.search.isInitialized()) { 161 | launchSearchFromQueryString(); 162 | } 163 | }); 164 | 165 | gitbook.events.on('search.ready', function() { 166 | bindSearch(); 167 | 168 | // Launch search from query param at start 169 | launchSearchFromQueryString(); 170 | }); 171 | 172 | function getParameterByName(name) { 173 | var url = window.location.href; 174 | name = name.replace(/[\[\]]/g, '\\$&'); 175 | var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)', 'i'), 176 | results = regex.exec(url); 177 | if (!results) return null; 178 | if (!results[2]) return ''; 179 | return decodeURIComponent(results[2].replace(/\+/g, ' ')); 180 | } 181 | 182 | function updateQueryString(key, value) { 183 | value = encodeURIComponent(value); 184 | 185 | var url = window.location.href; 186 | var re = new RegExp('([?&])' + key + '=.*?(&|#|$)(.*)', 'gi'), 187 | hash; 188 | 189 | if (re.test(url)) { 190 | if (typeof value !== 'undefined' && value !== null) 191 | return url.replace(re, '$1' + key + '=' + value + '$2$3'); 192 | else { 193 | hash = url.split('#'); 194 | url = hash[0].replace(re, '$1$3').replace(/(&|\?)$/, ''); 195 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 196 | url += '#' + hash[1]; 197 | return url; 198 | } 199 | } 200 | else { 201 | if (typeof value !== 'undefined' && value !== null) { 202 | var separator = url.indexOf('?') !== -1 ? '&' : '?'; 203 | hash = url.split('#'); 204 | url = hash[0] + separator + key + '=' + value; 205 | if (typeof hash[1] !== 'undefined' && hash[1] !== null) 206 | url += '#' + hash[1]; 207 | return url; 208 | } 209 | else 210 | return url; 211 | } 212 | } 213 | }); 214 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-fontsettings/fontsettings.js: -------------------------------------------------------------------------------- 1 | require(['gitbook', 'jquery'], function(gitbook, $) { 2 | // Configuration 3 | var MAX_SIZE = 4, 4 | MIN_SIZE = 0, 5 | BUTTON_ID; 6 | 7 | // Current fontsettings state 8 | var fontState; 9 | 10 | // Default themes 11 | var THEMES = [ 12 | { 13 | config: 'white', 14 | text: 'White', 15 | id: 0 16 | }, 17 | { 18 | config: 'sepia', 19 | text: 'Sepia', 20 | id: 1 21 | }, 22 | { 23 | config: 'night', 24 | text: 'Night', 25 | id: 2 26 | } 27 | ]; 28 | 29 | // Default font families 30 | var FAMILIES = [ 31 | { 32 | config: 'serif', 33 | text: 'Serif', 34 | id: 0 35 | }, 36 | { 37 | config: 'sans', 38 | text: 'Sans', 39 | id: 1 40 | } 41 | ]; 42 | 43 | // Return configured themes 44 | function getThemes() { 45 | return THEMES; 46 | } 47 | 48 | // Modify configured themes 49 | function setThemes(themes) { 50 | THEMES = themes; 51 | updateButtons(); 52 | } 53 | 54 | // Return configured font families 55 | function getFamilies() { 56 | return FAMILIES; 57 | } 58 | 59 | // Modify configured font families 60 | function setFamilies(families) { 61 | FAMILIES = families; 62 | updateButtons(); 63 | } 64 | 65 | // Save current font settings 66 | function saveFontSettings() { 67 | gitbook.storage.set('fontState', fontState); 68 | update(); 69 | } 70 | 71 | // Increase font size 72 | function enlargeFontSize(e) { 73 | e.preventDefault(); 74 | if (fontState.size >= MAX_SIZE) return; 75 | 76 | fontState.size++; 77 | saveFontSettings(); 78 | } 79 | 80 | // Decrease font size 81 | function reduceFontSize(e) { 82 | e.preventDefault(); 83 | if (fontState.size <= MIN_SIZE) return; 84 | 85 | fontState.size--; 86 | saveFontSettings(); 87 | } 88 | 89 | // Change font family 90 | function changeFontFamily(configName, e) { 91 | if (e && e instanceof Event) { 92 | e.preventDefault(); 93 | } 94 | 95 | var familyId = getFontFamilyId(configName); 96 | fontState.family = familyId; 97 | saveFontSettings(); 98 | } 99 | 100 | // Change type of color theme 101 | function changeColorTheme(configName, e) { 102 | if (e && e instanceof Event) { 103 | e.preventDefault(); 104 | } 105 | 106 | var $book = gitbook.state.$book; 107 | 108 | // Remove currently applied color theme 109 | if (fontState.theme !== 0) 110 | $book.removeClass('color-theme-'+fontState.theme); 111 | 112 | // Set new color theme 113 | var themeId = getThemeId(configName); 114 | fontState.theme = themeId; 115 | if (fontState.theme !== 0) 116 | $book.addClass('color-theme-'+fontState.theme); 117 | 118 | saveFontSettings(); 119 | } 120 | 121 | // Return the correct id for a font-family config key 122 | // Default to first font-family 123 | function getFontFamilyId(configName) { 124 | // Search for plugin configured font family 125 | var configFamily = $.grep(FAMILIES, function(family) { 126 | return family.config == configName; 127 | })[0]; 128 | // Fallback to default font family 129 | return (!!configFamily)? configFamily.id : 0; 130 | } 131 | 132 | // Return the correct id for a theme config key 133 | // Default to first theme 134 | function getThemeId(configName) { 135 | // Search for plugin configured theme 136 | var configTheme = $.grep(THEMES, function(theme) { 137 | return theme.config == configName; 138 | })[0]; 139 | // Fallback to default theme 140 | return (!!configTheme)? configTheme.id : 0; 141 | } 142 | 143 | function update() { 144 | var $book = gitbook.state.$book; 145 | 146 | $('.font-settings .font-family-list li').removeClass('active'); 147 | $('.font-settings .font-family-list li:nth-child('+(fontState.family+1)+')').addClass('active'); 148 | 149 | $book[0].className = $book[0].className.replace(/\bfont-\S+/g, ''); 150 | $book.addClass('font-size-'+fontState.size); 151 | $book.addClass('font-family-'+fontState.family); 152 | 153 | if(fontState.theme !== 0) { 154 | $book[0].className = $book[0].className.replace(/\bcolor-theme-\S+/g, ''); 155 | $book.addClass('color-theme-'+fontState.theme); 156 | } 157 | } 158 | 159 | function init(config) { 160 | // Search for plugin configured font family 161 | var configFamily = getFontFamilyId(config.family), 162 | configTheme = getThemeId(config.theme); 163 | 164 | // Instantiate font state object 165 | fontState = gitbook.storage.get('fontState', { 166 | size: config.size || 2, 167 | family: configFamily, 168 | theme: configTheme 169 | }); 170 | 171 | update(); 172 | } 173 | 174 | function updateButtons() { 175 | // Remove existing fontsettings buttons 176 | if (!!BUTTON_ID) { 177 | gitbook.toolbar.removeButton(BUTTON_ID); 178 | } 179 | 180 | // Create buttons in toolbar 181 | BUTTON_ID = gitbook.toolbar.createButton({ 182 | icon: 'fa fa-font', 183 | label: 'Font Settings', 184 | className: 'font-settings', 185 | dropdown: [ 186 | [ 187 | { 188 | text: 'A', 189 | className: 'font-reduce', 190 | onClick: reduceFontSize 191 | }, 192 | { 193 | text: 'A', 194 | className: 'font-enlarge', 195 | onClick: enlargeFontSize 196 | } 197 | ], 198 | $.map(FAMILIES, function(family) { 199 | family.onClick = function(e) { 200 | return changeFontFamily(family.config, e); 201 | }; 202 | 203 | return family; 204 | }), 205 | $.map(THEMES, function(theme) { 206 | theme.onClick = function(e) { 207 | return changeColorTheme(theme.config, e); 208 | }; 209 | 210 | return theme; 211 | }) 212 | ] 213 | }); 214 | } 215 | 216 | // Init configuration at start 217 | gitbook.events.bind('start', function(e, config) { 218 | var opts = config.fontsettings; 219 | 220 | // Generate buttons at start 221 | updateButtons(); 222 | 223 | // Init current settings 224 | init(opts); 225 | }); 226 | 227 | // Expose API 228 | gitbook.fontsettings = { 229 | enlargeFontSize: enlargeFontSize, 230 | reduceFontSize: reduceFontSize, 231 | setTheme: changeColorTheme, 232 | setFamily: changeFontFamily, 233 | getThemes: getThemes, 234 | setThemes: setThemes, 235 | getFamilies: getFamilies, 236 | setFamilies: setFamilies 237 | }; 238 | }); 239 | 240 | 241 | -------------------------------------------------------------------------------- /images/react-basics.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 63 | 70 | NewVirtual DOM 86 | 93 | New Data 104 | 111 | 118 | React compares Virtual DOM & DOM 134 | 139 | 146 | React UpdatesDOM 162 | 163 | 164 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-fontsettings/website.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Theme 1 3 | */ 4 | .color-theme-1 .dropdown-menu { 5 | background-color: #111111; 6 | border-color: #7e888b; 7 | } 8 | .color-theme-1 .dropdown-menu .dropdown-caret .caret-inner { 9 | border-bottom: 9px solid #111111; 10 | } 11 | .color-theme-1 .dropdown-menu .buttons { 12 | border-color: #7e888b; 13 | } 14 | .color-theme-1 .dropdown-menu .button { 15 | color: #afa790; 16 | } 17 | .color-theme-1 .dropdown-menu .button:hover { 18 | color: #73553c; 19 | } 20 | /* 21 | * Theme 2 22 | */ 23 | .color-theme-2 .dropdown-menu { 24 | background-color: #2d3143; 25 | border-color: #272a3a; 26 | } 27 | .color-theme-2 .dropdown-menu .dropdown-caret .caret-inner { 28 | border-bottom: 9px solid #2d3143; 29 | } 30 | .color-theme-2 .dropdown-menu .buttons { 31 | border-color: #272a3a; 32 | } 33 | .color-theme-2 .dropdown-menu .button { 34 | color: #62677f; 35 | } 36 | .color-theme-2 .dropdown-menu .button:hover { 37 | color: #f4f4f5; 38 | } 39 | .book .book-header .font-settings .font-enlarge { 40 | line-height: 30px; 41 | font-size: 1.4em; 42 | } 43 | .book .book-header .font-settings .font-reduce { 44 | line-height: 30px; 45 | font-size: 1em; 46 | } 47 | .book.color-theme-1 .book-body { 48 | color: #704214; 49 | background: #f3eacb; 50 | } 51 | .book.color-theme-1 .book-body .page-wrapper .page-inner section { 52 | background: #f3eacb; 53 | } 54 | .book.color-theme-2 .book-body { 55 | color: #bdcadb; 56 | background: #1c1f2b; 57 | } 58 | .book.color-theme-2 .book-body .page-wrapper .page-inner section { 59 | background: #1c1f2b; 60 | } 61 | .book.font-size-0 .book-body .page-inner section { 62 | font-size: 1.2rem; 63 | } 64 | .book.font-size-1 .book-body .page-inner section { 65 | font-size: 1.4rem; 66 | } 67 | .book.font-size-2 .book-body .page-inner section { 68 | font-size: 1.6rem; 69 | } 70 | .book.font-size-3 .book-body .page-inner section { 71 | font-size: 2.2rem; 72 | } 73 | .book.font-size-4 .book-body .page-inner section { 74 | font-size: 4rem; 75 | } 76 | .book.font-family-0 { 77 | font-family: Georgia, serif; 78 | } 79 | .book.font-family-1 { 80 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 81 | } 82 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal { 83 | color: #704214; 84 | } 85 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal a { 86 | color: inherit; 87 | } 88 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 89 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2, 90 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h3, 91 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h4, 92 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h5, 93 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 94 | color: inherit; 95 | } 96 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h1, 97 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h2 { 98 | border-color: inherit; 99 | } 100 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal h6 { 101 | color: inherit; 102 | } 103 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal hr { 104 | background-color: inherit; 105 | } 106 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal blockquote { 107 | border-color: inherit; 108 | } 109 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal pre, 110 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal code { 111 | background: #fdf6e3; 112 | color: #657b83; 113 | border-color: #f8df9c; 114 | } 115 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal .highlight { 116 | background-color: inherit; 117 | } 118 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table th, 119 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table td { 120 | border-color: #f5d06c; 121 | } 122 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr { 123 | color: inherit; 124 | background-color: #fdf6e3; 125 | border-color: #444444; 126 | } 127 | .book.color-theme-1 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 128 | background-color: #fbeecb; 129 | } 130 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal { 131 | color: #bdcadb; 132 | } 133 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal a { 134 | color: #3eb1d0; 135 | } 136 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 137 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2, 138 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h3, 139 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h4, 140 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h5, 141 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 142 | color: #fffffa; 143 | } 144 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h1, 145 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h2 { 146 | border-color: #373b4e; 147 | } 148 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal h6 { 149 | color: #373b4e; 150 | } 151 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal hr { 152 | background-color: #373b4e; 153 | } 154 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal blockquote { 155 | border-color: #373b4e; 156 | } 157 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal pre, 158 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal code { 159 | color: #9dbed8; 160 | background: #2d3143; 161 | border-color: #2d3143; 162 | } 163 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal .highlight { 164 | background-color: #282a39; 165 | } 166 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table th, 167 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table td { 168 | border-color: #3b3f54; 169 | } 170 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr { 171 | color: #b6c2d2; 172 | background-color: #2d3143; 173 | border-color: #3b3f54; 174 | } 175 | .book.color-theme-2 .book-body .page-wrapper .page-inner section.normal table tr:nth-child(2n) { 176 | background-color: #35394b; 177 | } 178 | .book.color-theme-1 .book-header { 179 | color: #afa790; 180 | background: transparent; 181 | } 182 | .book.color-theme-1 .book-header .btn { 183 | color: #afa790; 184 | } 185 | .book.color-theme-1 .book-header .btn:hover { 186 | color: #73553c; 187 | background: none; 188 | } 189 | .book.color-theme-1 .book-header h1 { 190 | color: #704214; 191 | } 192 | .book.color-theme-2 .book-header { 193 | color: #7e888b; 194 | background: transparent; 195 | } 196 | .book.color-theme-2 .book-header .btn { 197 | color: #3b3f54; 198 | } 199 | .book.color-theme-2 .book-header .btn:hover { 200 | color: #fffff5; 201 | background: none; 202 | } 203 | .book.color-theme-2 .book-header h1 { 204 | color: #bdcadb; 205 | } 206 | .book.color-theme-1 .book-body .navigation { 207 | color: #afa790; 208 | } 209 | .book.color-theme-1 .book-body .navigation:hover { 210 | color: #73553c; 211 | } 212 | .book.color-theme-2 .book-body .navigation { 213 | color: #383f52; 214 | } 215 | .book.color-theme-2 .book-body .navigation:hover { 216 | color: #fffff5; 217 | } 218 | /* 219 | * Theme 1 220 | */ 221 | .book.color-theme-1 .book-summary { 222 | color: #afa790; 223 | background: #111111; 224 | border-right: 1px solid rgba(0, 0, 0, 0.07); 225 | } 226 | .book.color-theme-1 .book-summary .book-search { 227 | background: transparent; 228 | } 229 | .book.color-theme-1 .book-summary .book-search input, 230 | .book.color-theme-1 .book-summary .book-search input:focus { 231 | border: 1px solid transparent; 232 | } 233 | .book.color-theme-1 .book-summary ul.summary li.divider { 234 | background: #7e888b; 235 | box-shadow: none; 236 | } 237 | .book.color-theme-1 .book-summary ul.summary li i.fa-check { 238 | color: #33cc33; 239 | } 240 | .book.color-theme-1 .book-summary ul.summary li.done > a { 241 | color: #877f6a; 242 | } 243 | .book.color-theme-1 .book-summary ul.summary li a, 244 | .book.color-theme-1 .book-summary ul.summary li span { 245 | color: #877f6a; 246 | background: transparent; 247 | font-weight: normal; 248 | } 249 | .book.color-theme-1 .book-summary ul.summary li.active > a, 250 | .book.color-theme-1 .book-summary ul.summary li a:hover { 251 | color: #704214; 252 | background: transparent; 253 | font-weight: normal; 254 | } 255 | /* 256 | * Theme 2 257 | */ 258 | .book.color-theme-2 .book-summary { 259 | color: #bcc1d2; 260 | background: #2d3143; 261 | border-right: none; 262 | } 263 | .book.color-theme-2 .book-summary .book-search { 264 | background: transparent; 265 | } 266 | .book.color-theme-2 .book-summary .book-search input, 267 | .book.color-theme-2 .book-summary .book-search input:focus { 268 | border: 1px solid transparent; 269 | } 270 | .book.color-theme-2 .book-summary ul.summary li.divider { 271 | background: #272a3a; 272 | box-shadow: none; 273 | } 274 | .book.color-theme-2 .book-summary ul.summary li i.fa-check { 275 | color: #33cc33; 276 | } 277 | .book.color-theme-2 .book-summary ul.summary li.done > a { 278 | color: #62687f; 279 | } 280 | .book.color-theme-2 .book-summary ul.summary li a, 281 | .book.color-theme-2 .book-summary ul.summary li span { 282 | color: #c1c6d7; 283 | background: transparent; 284 | font-weight: 600; 285 | } 286 | .book.color-theme-2 .book-summary ul.summary li.active > a, 287 | .book.color-theme-2 .book-summary ul.summary li a:hover { 288 | color: #f4f4f5; 289 | background: #252737; 290 | font-weight: 600; 291 | } 292 | -------------------------------------------------------------------------------- /images/clojurescript-platforms.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | 24 | 47 | 49 | 50 | 52 | image/svg+xml 53 | 55 | 56 | 57 | 58 | 59 | 64 | 72 | NashornJVM JavaScriptEngine 93 | 101 | NodejsV8 JavaScriptEngine 122 | 130 | BrowserJavaScriptEngine 151 | 181 | ClojureScript 192 | 197 | 202 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /clojurescript-code/index.md: -------------------------------------------------------------------------------- 1 | # ClojureScript code 2 | 3 | I have taken examples from a number of sources to give you a basic introduction to using Clojurescript. Please see [Clojurescript Unraveled](http://funcool.github.io/clojurescript-unraveled) for a more complete reference guide 4 | 5 | * Rafel Spacjer's blog: [Clojurescript: Javascript interop](http://www.spacjer.com/blog/2014/09/12/clojurescript-javascript-interop/) 6 | 7 | ## Defining scope 8 | 9 | 10 | ## Javascript scope included 11 | 12 | ClojureScript defines special js namespace to allow accessing JavaScript types/functions/methods/objects defined in global scope (i.e. window object for browser). 13 | 14 | 15 | ```cljs 16 | (def text js/globalName) ;; JS output: namespace.text = globalName; 17 | ``` 18 | 19 | Creating objects 20 | 21 | We can create JavaScript objects from ClojureScript by adding . to the end of constructor function: 22 | 23 | (def t1 (js/MyType.)) ;; JS output: namespace.t1 = new MyType; 24 | 25 | (note: at first I thought that this generated JS code was wrong, because of the lack of parentheses, but as it clarifies it’s valid - if constructor function doesn’t have arguments, then parentheses can be skipped) 26 | 27 | with arguments: 28 | 29 | (def t2 (js/MyComplexType. "Bob")) ;; JS output: namespace.t2 = new MyComplexType("Bob"); 30 | 31 | There is also a different way of creating objects, by using the new function (the name of JS constructor function should be without period): 32 | 33 | (def my-type (new js/MyComplexType "Bob")) ;; JS output: namespace.my_type = new MyComplexType("Bob"); 34 | 35 | Invoking methods 36 | 37 | To invoke a JavaScript method we need to prefix the name of the method with the . (dot): 38 | 39 | (.hello js/window) ;; JS output: window.hello(); 40 | 41 | which is a syntactic sugar of: 42 | 43 | (. js/window (hello)) 44 | 45 | To pass arguments to the function we do: 46 | 47 | (.helloAgain js/window "John") ;; JS output: window.helloAgain("John"); 48 | 49 | or 50 | 51 | (. js/window (helloAgain "John")) 52 | 53 | Same thing can be done with created object: 54 | 55 | (def my-type (js/MyComplexType. "Bob")) ;; JS output: namespace.my_type = new MyComplexType("Bob"); 56 | (.hello my-type) ;; JS output: namespace.my_type.hello(); 57 | 58 | Accessing properties 59 | 60 | ClojureScript provides a few ways of working with JavaScript properties. The simplest one is to use .- property access syntax: 61 | 62 | (def my-type (js/MyType.)) ;; JS output: namespace.my_type = new MyType; 63 | (def name (.-name my-type)) ;; JS output: namespace.name = namespace.my_type.name; 64 | 65 | similar thing can be achieved by aget function, which takes object and the name of the property (as a string) as arguments: 66 | 67 | (def name (aget my-type "name")) ;; JS output: namespace.name = namespace.my_type["name"]; 68 | 69 | The aget allows also accessing nested properties: 70 | 71 | (aget js/object "prop1" "prop2" "prop3") ;; JS output: object["prop1"]["prop2"]["prop3"]; 72 | 73 | the same thing (generated code is different) can be done by using .. syntax: 74 | 75 | (.. js/object -prop1 -prop2 -prop3) ;; JS output: object.prop1.prop2.prop3; 76 | 77 | You can also set a value of a property from the ClojureScript, to do this you can use aset or set! functions: 78 | 79 | The aset function takes name of the property as a string: 80 | 81 | (def my-type (js/MyType.)) 82 | (aset my-type "name" "Bob") ;; JS output: namespace.my_type["name"] = "Bob"; 83 | 84 | and the set! takes a property access: 85 | 86 | (set! (.-name my-type) "Andy") ;; JS output: namespace.my_type.name = "Andy"; 87 | 88 | Arrays 89 | 90 | The aget function can be also used for accessing JavaScript array element: 91 | 92 | (aget js/globalArray 1) ;; JS output: globalArray[1]; 93 | 94 | or if you want to get nested element you can use it in this way: 95 | 96 | (aget js/globalArray 3 1) ;; JS output: globalArray[3][1]; 97 | 98 | Nested scopes 99 | 100 | This subject was a bit confusing for me. In my project I wanted to translate such a code: 101 | 102 | var map = new Microsoft.Maps.Map(); 103 | 104 | to ClojureScript. As you can see the Map function is in nested scope. The idiomatic way of accessing nested properties is to use .. or aget functions but this can’t be done for constructor function. In such case, we need to use the dot notation (even it’s not idiomatic for Clojure code): 105 | 106 | (def m2 (js/Microsoft.Maps.Themes.BingTheme.)) 107 | 108 | or with the new function: 109 | 110 | (def m1 (new js/Microsoft.Maps.Themes.BingTheme)) 111 | 112 | If we write this expression like this: 113 | 114 | (def m3 (new (.. js/Microsoft -Maps -Themes -BingTheme))) 115 | 116 | we will get an exception: 117 | 118 | First arg to new must be a symbol at line 119 | core.clj:4403 clojure.core/ex-info 120 | analyzer.clj:268 cljs.analyzer/error 121 | analyzer.clj:265 cljs.analyzer/error 122 | analyzer.clj:908 cljs.analyzer/eval1316[fn] 123 | MultiFn.java:241 clojure.lang.MultiFn.invoke 124 | analyzer.clj:1444 cljs.analyzer/analyze-seq 125 | analyzer.clj:1532 cljs.analyzer/analyze[fn] 126 | analyzer.clj:1525 cljs.analyzer/analyze 127 | analyzer.clj:609 cljs.analyzer/eval1188[fn] 128 | analyzer.clj:608 cljs.analyzer/eval1188[fn] 129 | MultiFn.java:241 clojure.lang.MultiFn.invoke 130 | analyzer.clj:1444 cljs.analyzer/analyze-seq 131 | analyzer.clj:1532 cljs.analyzer/analyze[fn] 132 | analyzer.clj:1525 cljs.analyzer/analyze 133 | analyzer.clj:1520 cljs.analyzer/analyze 134 | compiler.clj:908 cljs.compiler/compile-file* 135 | compiler.clj:1022 cljs.compiler/compile-file 136 | 137 | Creating JavaScript objects 138 | 139 | There are many cases where we need to pass JavaScript object to a method from ClojureScript. In general ClojureScript works with its own data structures (immutable, persistent vector, map, set etc.) that can be converted to plain JS objects. There are several ways of doing it. 140 | 141 | If we want to create a simple JavaScript object from the list of key value pairs we can use js-obj macro: 142 | 143 | (def my-object (js-obj "a" 1 "b" true "c" nil)) ;; JS output: namespace.my_object_4 = (function (){var obj6284 = {"a":(1),"b":true,"c":null};return obj6284; 144 | 145 | Note that js-obj forces you to use strings as keys and basic data literals (string, number, boolean) as values. The ClojureScript data structures won’t be changed, so this: 146 | 147 | (def js-object (js-obj :a 1 :b [1 2 3] :c #{"d" true :e nil})) 148 | 149 | will create such JavaScript object: 150 | 151 | { 152 | ":c" cljs.core.PersistentHashSet, 153 | ":b" cljs.core.PersistentVector, 154 | ":a" 1 155 | } 156 | 157 | as you can see there are used internal types such as: 158 | cljs.core.PersistentHashSet 159 | cljs.core.PersistentVector 160 | and the ClojureScript keyword was changed to string prefixed with colon. 161 | 162 | To solve this problem we can use clj->js function that: 163 | “Recursively transforms ClojureScript values to JavaScript. 164 | sets/vectors/lists become Arrays, Keywords and Symbol become Strings, 165 | Maps become Objects.” 166 | 167 | (def js-object (clj->js :a 1 :b [1 2 3] :c #{"d" true :e nil})) 168 | 169 | will produce such object: 170 | 171 | { 172 | "a": 1, 173 | "b": [1, 2, 3], 174 | "c": [null, "d", "e", true] 175 | } 176 | 177 | There is also one more way of producing JavaScript objects - we can use #js reader literal: 178 | 179 | (def js-object #js {:a 1 :b 2}) 180 | 181 | which generates code: 182 | 183 | namespace.core.js_object = {"b": (2), "a": (1)}; 184 | 185 | When working with #js you need to be cautious, because this literal also won’t transform inner structures (it’s shallow): 186 | 187 | (def js-object #js {:a 1 :b [1 2 3] :c {"d" true :e nil}}) 188 | 189 | will create such object: 190 | 191 | { 192 | "c": cljs.core.PersistentArrayMap, 193 | "b": cljs.core.PersistentVector, 194 | "a": 1 195 | } 196 | 197 | to solve this you need to add #js before every ClojureScript structure: 198 | 199 | (def js-object #js {:a 1 :b #js [1 2 3] :c #js ["d" true :e nil]}) 200 | 201 | JavaScript object: 202 | 203 | { 204 | "c": { 205 | "e": null, 206 | "d": true 207 | }, 208 | "b": [1, 2, 3 ], 209 | "a": 1 210 | } 211 | 212 | Using JavaScript objects 213 | 214 | There are situations when we need to convert JavaScript object or array into ClojureScript data structure. We can do this by using js->clj function that: 215 | "Recursively transforms JavaScript arrays into ClojureScript vectors, and JavaScript objects into ClojureScript maps. With 216 | option ‘:keywordize-keys true’ will convert object fields from 217 | strings to keywords. 218 | 219 | (def my-array (js->clj (.-globalArray js/window))) 220 | (def first-item (get my-array 0)) ;; 1 221 | 222 | (def my-obj (js->clj (.-globalObject js/window))) 223 | (def a (get my-obj "a")) ;; 1 224 | 225 | as the function doc states we can use :keywordize-keys true to convert string keys of created map to keywords: 226 | 227 | (def my-obj-2 (js->clj (.-globalObject js/window) :keywordize-keys true)) 228 | (def a-2 (:a my-obj-2)) ;; 1 229 | 230 | Addition 231 | 232 | If all other methods of working with JavaScript failed, there is a js* that takes a string as an argument and emits it as a JavaScript code: 233 | 234 | (js* "alert('my special JS code')") ;; JS output: alert('my special JS code'); 235 | 236 | Exposing ClojureScript functions 237 | 238 | It is worth noting that the exact form of JavaScript code generated from ClojureScript depends on compiler settings. Those settings can be defined in Leiningen project.clj file: 239 | 240 | Part of project.clj file: 241 | 242 | :cljsbuild { 243 | :builds [{:id "dev" 244 | :source-paths ["src"] 245 | :compiler { 246 | :main your-namespace.core 247 | :output-to "out/your-namespace.js" 248 | :output-dir "out" 249 | :optimizations :none 250 | :cache-analysis true 251 | :source-map true}} 252 | {:id "release" 253 | :source-paths ["src"] 254 | :compiler { 255 | :main blog-sc-testing.core 256 | :output-to "out-adv/your-namespace.min.js" 257 | :output-dir "out-adv" 258 | :optimizations :advanced 259 | :pretty-print false}}]} 260 | 261 | As you can see above, there are two defined builds: dev and release. Please note the :optimizations parameter - for :advanced value the code will be truncated (not used code is removed) and renamed (shorter names are used). 262 | 263 | For example, this ClojureScript code: 264 | 265 | (defn add-numbers [a b] 266 | (+ a b)) 267 | 268 | will be compiled to such JavaScript code in :advanced mode: 269 | 270 | function yg(a,b){return a+b} 271 | 272 | The function name is completely “random”, so you can’t use it from JavaScript file. To be able to use defined in ClojureScript functions (with their original names) you should mark them with the :export metadata: 273 | 274 | (defn ^:export add-numbers [a b] 275 | (+ a b)) 276 | 277 | The :export keyword tells compiler to export given function name to the outside world. (This is done by exportSymbol function from Google Closure Compiler - but I won’t go into the details). Then in your external JavaScript code you can invoke this function: 278 | 279 | your_namespace.core.add_numbers(1,2); 280 | 281 | Please, notice that all dashes were replaced by underscors. 282 | Using external JavaScript libraries 283 | 284 | The :advanced mode affects also invocation of the external libreries, because all functions/methods names are changed to minimal form. Lets take a ClojureScript code, that invokes PolarArea function form the Chart object: 285 | 286 | (defn ^:export creat-chart [] 287 | (let [ch (js/Chart.)] 288 | (. ch (PolarArea [])))) 289 | 290 | after compilation this code will look similar to this one: 291 | 292 | function(){return(new Chart).Bc(zc)} 293 | 294 | As you can see, the PolarArea method was changed to Bc name, which of course will cause runtime error. To prevent this, we need to tell compiler which names shouldn’t be changed. Those names should be defined in external JavaScript file (i.e. externs.js) and provided to the compiler. For our example the externs.js file should look like this one: 295 | 296 | var Chart = {}; 297 | Chart.PolarArea = function() {}; 298 | 299 | The compiler should be informed about this file by :externs setting in project.clj file: 300 | 301 | {:id "release" 302 | :source-paths ["src"] 303 | :compiler { 304 | :main blog-sc-testing.core 305 | :output-to "out-adv/your-namespace.min.js" 306 | :output-dir "out-adv" 307 | :optimizations :advanced 308 | :externs ["externs.js"] 309 | :pretty-print false}} 310 | 311 | If we do all those things, created JavaScript code will contain correct invocation of PolarArea function: 312 | 313 | function(){return(new Chart).PolarArea(Ec)} 314 | 315 | To get more details about using external JavaScript libraries in ClojureScript I recommend you to read excellent blog post by Luke VanderHart about this. 316 | 317 | 318 | 319 | # Interop 320 | 321 | (. js/document (getElementById "app")) 322 | 323 | (.-value input) 324 | 325 | 326 | ## core.async - communicating sequential processes 327 | 328 | * processes 329 | * channels 330 | * coordination 331 | 332 | using core.async allows you to write sequential logic that avoids the need for callback hell of hand-crafting asyncronous coding. 333 | 334 | ;; sudo code 335 | (str "do something") 336 | (send-to-channel) 337 | (str "keep doing something") 338 | 339 | ;; put something on the channel 340 | (put!) 341 | (>!) 342 | 343 | ;; get something from the channel 344 | (take!) 345 | ( 2 | 3 | 4 | 22 | 24 | 46 | 48 | 49 | 51 | image/svg+xml 52 | 54 | 55 | 56 | 57 | 58 | 63 | 66 | 74 | 77 | 85 | 88 | React.js 99 | 102 | 107 | 108 | 112 | 113 | 119 | 120 | 124 | 125 | 126 | 127 | 128 | reagent 139 | 140 | 143 | 151 | re-frame 162 | 163 | 165 | 173 | 181 | Browser 192 | DOM 203 | 204 | 208 | 211 | 215 | 219 | 229 | 239 | 240 | 241 | 251 | 252 | 253 | 254 | 255 | -------------------------------------------------------------------------------- /gitbook/gitbook-plugin-lunr/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.5.12 3 | * Copyright (C) 2015 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.5.12",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(t){return arguments.length&&null!=t&&void 0!=t?Array.isArray(t)?t.map(function(t){return t.toLowerCase()}):t.toString().trim().toLowerCase().split(/[\s\-]+/):[]},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,o=0;n>o;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;no;o++){for(var r=t[o],s=0;i>s&&(r=this._stack[s](r,o,t),void 0!==r);s++);void 0!==r&&e.push(r)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(r===t)return o;t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o]}return r===t?o:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,o=e+Math.floor(i/2),r=this.elements[o];i>1;)t>r&&(e=o),r>t&&(n=o),i=n-e,o=e+Math.floor(i/2),r=this.elements[o];return r>t?o:t>r?o+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,o=0,r=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>r-1||o>s-1)break;a[i]!==h[o]?a[i]h[o]&&o++:(n.add(a[i]),i++,o++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;return this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone(),i.add.apply(i,n.toArray()),i},t.SortedSet.prototype.toJSON=function(){return this.toArray()},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.Store,this.tokenStore=new t.TokenStore,this.corpusTokens=new t.SortedSet,this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var t=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,t)},t.Index.prototype.off=function(t,e){return this.eventEmitter.removeListener(t,e)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;return n._fields=e.fields,n._ref=e.ref,n.documentStore=t.Store.load(e.documentStore),n.tokenStore=t.TokenStore.load(e.tokenStore),n.corpusTokens=t.SortedSet.load(e.corpusTokens),n.pipeline=t.Pipeline.load(e.pipeline),n},t.Index.prototype.field=function(t,e){var e=e||{},n={name:t,boost:e.boost||1};return this._fields.push(n),this},t.Index.prototype.ref=function(t){return this._ref=t,this},t.Index.prototype.add=function(e,n){var i={},o=new t.SortedSet,r=e[this._ref],n=void 0===n?!0:n;this._fields.forEach(function(n){var r=this.pipeline.run(t.tokenizer(e[n.name]));i[n.name]=r,t.SortedSet.prototype.add.apply(o,r)},this),this.documentStore.set(r,o),t.SortedSet.prototype.add.apply(this.corpusTokens,o.toArray());for(var s=0;s0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(t.tokenizer(e)),i=new t.Vector,o=[],r=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*r,h=this,l=this.tokenStore.expand(e).reduce(function(n,o){var r=h.corpusTokens.indexOf(o),s=h.idf(o),l=1,u=new t.SortedSet;if(o!==e){var c=Math.max(3,o.length-e.length);l=1/Math.log(c)}return r>-1&&i.insert(r,a*s*l),Object.keys(h.tokenStore.get(o)).forEach(function(t){u.add(t)}),n.union(u)},new t.SortedSet);o.push(l)},this);var a=o.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,o=new t.Vector,r=0;i>r;r++){var s=n.elements[r],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);o.insert(this.corpusTokens.indexOf(s),a*h)}return o},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",o=n+"[^aeiouy]*",r=i+"[aeiou]*",s="^("+o+")?"+r+o,a="^("+o+")?"+r+o+"("+r+")?$",h="^("+o+")?"+r+o+r+o,l="^("+o+")?"+i,u=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(l),p=/^(.+?)(ss|i)es$/,m=/^(.+?)([^s])s$/,v=/^(.+?)eed$/,y=/^(.+?)(ed|ing)$/,g=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),x=new RegExp("^"+o+i+"[^aeiouwxy]$"),k=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,_=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,F=/^(.+?)(s|t)(ion)$/,O=/^(.+?)e$/,P=/ll$/,N=new RegExp("^"+o+i+"[^aeiouwxy]$"),T=function(n){var i,o,r,s,a,h,l;if(n.length<3)return n;if(r=n.substr(0,1),"y"==r&&(n=r.toUpperCase()+n.substr(1)),s=p,a=m,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=v,a=y,s.test(n)){var T=s.exec(n);s=u,s.test(T[1])&&(s=g,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,l=x,a.test(n)?n+="e":h.test(n)?(s=g,n=n.replace(s,"")):l.test(n)&&(n+="e"))}if(s=k,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+t[o])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],o=T[2],s=u,s.test(i)&&(n=i+e[o])}if(s=_,a=F,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=O,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=N,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=P,a=c,s.test(n)&&a.test(n)&&(s=g,n=n.replace(s,"")),"y"==r&&(n=r.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.stopWordFilter=function(e){return e&&t.stopWordFilter.stopWords[e]!==e?e:void 0},t.stopWordFilter.stopWords={a:"a",able:"able",about:"about",across:"across",after:"after",all:"all",almost:"almost",also:"also",am:"am",among:"among",an:"an",and:"and",any:"any",are:"are",as:"as",at:"at",be:"be",because:"because",been:"been",but:"but",by:"by",can:"can",cannot:"cannot",could:"could",dear:"dear",did:"did","do":"do",does:"does",either:"either","else":"else",ever:"ever",every:"every","for":"for",from:"from",get:"get",got:"got",had:"had",has:"has",have:"have",he:"he",her:"her",hers:"hers",him:"him",his:"his",how:"how",however:"however",i:"i","if":"if","in":"in",into:"into",is:"is",it:"it",its:"its",just:"just",least:"least",let:"let",like:"like",likely:"likely",may:"may",me:"me",might:"might",most:"most",must:"must",my:"my",neither:"neither",no:"no",nor:"nor",not:"not",of:"of",off:"off",often:"often",on:"on",only:"only",or:"or",other:"other",our:"our",own:"own",rather:"rather",said:"said",say:"say",says:"says",she:"she",should:"should",since:"since",so:"so",some:"some",than:"than",that:"that",the:"the",their:"their",them:"them",then:"then",there:"there",these:"these",they:"they","this":"this",tis:"tis",to:"to",too:"too",twas:"twas",us:"us",wants:"wants",was:"was",we:"we",were:"were",what:"what",when:"when",where:"where",which:"which","while":"while",who:"who",whom:"whom",why:"why",will:"will","with":"with",would:"would",yet:"yet",you:"you",your:"your"},t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){var e=t.replace(/^\W+/,"").replace(/\W+$/,"");return""===e?void 0:e},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t[0],o=t.slice(1);return i in n||(n[i]={docs:{}}),0===o.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(o,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n