├── .gitignore ├── plotSVG ├── img │ └── plot.png ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ ├── chart.cljs │ └── plot_svg.cljs ├── lens ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── lens.cljs ├── contacts ├── .gitignore ├── public │ ├── css │ │ └── main.css │ └── index.html ├── package.json ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── contacts.cljs ├── hoplife ├── .gitignore ├── package.json ├── public │ ├── css │ │ └── main.css │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── hoplife.cljs ├── inputs ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md ├── src │ ├── index.cljs.hl │ └── demo │ │ └── inputs.cljs └── package-lock.json ├── tictactoe ├── .gitignore ├── package.json ├── public │ ├── index.html │ └── css │ │ └── main.css ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ ├── tictactoe │ ├── advanced.cljs │ ├── basic.cljs │ └── element.cljs │ └── tictactoe.cljs ├── todoFRP ├── .gitignore ├── public │ ├── image │ │ └── bg.png │ ├── index.html │ └── css │ │ └── main.css ├── package.json ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── todo_frp.cljs ├── tworker ├── .gitignore ├── package.json ├── public │ └── index.html ├── src │ └── demo │ │ ├── demo_worker.cljs │ │ └── tworker.cljs ├── README.md └── shadow-cljs.edn ├── async-webinar ├── .gitignore ├── package.json ├── public │ ├── index.html │ └── css │ │ └── main.css ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── async_webinar.cljs ├── demos-homepage ├── .gitignore ├── package.json ├── src │ ├── macros │ │ └── core.clj │ ├── demo │ │ └── homepage.cljs │ └── hoplon │ │ └── twitter │ │ └── bootstrap.cljs ├── shadow-cljs.edn ├── public │ ├── index.html │ ├── css │ │ └── main.css │ └── image │ │ └── Lambda_lc.svg └── README.md ├── validated-form ├── .gitignore ├── src │ ├── vform │ │ └── core.clj │ └── demo │ │ └── validated_form.cljs ├── package.json ├── shadow-cljs.edn ├── public │ ├── index.html │ └── css │ │ └── main.css └── README.md ├── w3c-worker ├── .gitignore ├── package.json ├── src │ ├── counter_worker │ │ └── counts.cljs │ └── demo │ │ └── w3c_worker.cljs ├── public │ └── index.html ├── README.md └── shadow-cljs.edn ├── infinite-scroll ├── .gitignore ├── package.json ├── public │ ├── index.html │ └── css │ │ └── main.css ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── infinite_scroll.cljs ├── counters ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── counter.cljs ├── infinite-scroll-paginated ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── infinite_scroll_paginated.cljs ├── Makefile ├── duracell ├── .gitignore ├── package.json ├── public │ └── index.html ├── shadow-cljs.edn ├── README.md └── src │ └── demo │ └── duracell.cljs ├── README.md └── epl-v10.html /.gitignore: -------------------------------------------------------------------------------- 1 | .gh-pages/ 2 | -------------------------------------------------------------------------------- /plotSVG/img/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoplon/demos/HEAD/plotSVG/img/plot.png -------------------------------------------------------------------------------- /lens/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /contacts/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /hoplife/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /inputs/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /plotSVG/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /tictactoe/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /todoFRP/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /todoFRP/public/image/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hoplon/demos/HEAD/todoFRP/public/image/bg.png -------------------------------------------------------------------------------- /tworker/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /async-webinar/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /demos-homepage/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /validated-form/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /w3c-worker/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /infinite-scroll/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /counters/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .cpcache/ 3 | .nrepl-* 4 | .shadow-cljs/ 5 | node_modules/ 6 | public/js/ 7 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/js/ 6 | -------------------------------------------------------------------------------- /contacts/public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 20px; 4 | } 5 | 6 | h1 { 7 | color: blue; 8 | } 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | help: 2 | @echo "USAGE: make [help|deploy]" 3 | 4 | deploy: 5 | cp -R .gh-pages/demos-homepage/* .gh-pages/ 6 | ghp-import -p .gh-pages 7 | -------------------------------------------------------------------------------- /contacts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /counters/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /hoplife/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /inputs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lens/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tictactoe/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /w3c-worker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demos-homepage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /infinite-scroll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /duracell/.gitignore: -------------------------------------------------------------------------------- 1 | .clj-kondo/**/ 2 | .nrepl-* 3 | .shadow-cljs/ 4 | node_modules/ 5 | public/cljs-runtime/ 6 | public/dcells.js 7 | public/main.js 8 | public/shared.js 9 | public/manifest.edn 10 | -------------------------------------------------------------------------------- /validated-form/src/vform/core.clj: -------------------------------------------------------------------------------- 1 | (ns vform.core 2 | (:require [javelin.core :refer [defc=]])) 3 | 4 | (defmacro defv [i c v] 5 | `(defc= ~i {:valid? (if (~v ~c) true false) 6 | :value ~c})) 7 | -------------------------------------------------------------------------------- /hoplife/public/css/main.css: -------------------------------------------------------------------------------- 1 | table,th,td { margin:0; padding:0; border-spacing:0; } 2 | table { border:1px solid black; } 3 | td { width:16px; height:16px; cursor:pointer; } 4 | td.alive { background-color: black; } 5 | -------------------------------------------------------------------------------- /duracell/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /plotSVG/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /todoFRP/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tworker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /async-webinar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /validated-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hoplon-demo", 3 | "version": "0.0.1", 4 | "private": true, 5 | "devDependencies": { 6 | "shadow-cljs": "2.25.8" 7 | }, 8 | "dependencies": { 9 | "jquery": "^3.7.0" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /counters/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Counters 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /lens/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AATree • Lens 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /plotSVG/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Plot SVG Chart 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /w3c-worker/src/counter_worker/counts.cljs: -------------------------------------------------------------------------------- 1 | (ns counter-worker.counts) 2 | 3 | (def i (atom 0)) 4 | 5 | (defn timed-count [] 6 | (swap! i + 1) 7 | (.postMessage js/self @i) 8 | (.setTimeout js/self timed-count 500)) 9 | 10 | (defn main [] 11 | (timed-count)) 12 | -------------------------------------------------------------------------------- /duracell/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Duracell demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tworker/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tworker demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /w3c-worker/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | W3C Worker 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /contacts/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Contacts 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /hoplife/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hoplife 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /todoFRP/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hoplon • TodoMVC 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /inputs/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Inputs - Hoplon Demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /async-webinar/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Async Webinar Demo 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /infinite-scroll/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Infinite scroll 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /tictactoe/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hoplon • Tic Tac Toe 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Infinite scroll paginated 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /demos-homepage/src/macros/core.clj: -------------------------------------------------------------------------------- 1 | (ns macros.core 2 | (:require 3 | [clojure.java.io :as io])) 4 | 5 | (defmacro demo-dirs [] 6 | (->> (. (io/file "../") listFiles) 7 | (filter #(and (.isDirectory %) 8 | (not (. (.getName %) (startsWith "."))) 9 | (not (.exists (io/file % ".no-demo"))))) 10 | (map #(.getName %)) 11 | sort 12 | vec)) 13 | -------------------------------------------------------------------------------- /inputs/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.inputs/init}} 8 | :devtools {:before-load demo.inputs/stop 9 | :after-load demo.inputs/start}}}} 10 | -------------------------------------------------------------------------------- /contacts/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.contacts/init}} 8 | :devtools {:before-load demo.contacts/stop 9 | :after-load demo.contacts/start}}}} 10 | -------------------------------------------------------------------------------- /counters/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.counter/init}} 8 | :devtools {:before-load demo.counter/stop 9 | :after-load demo.counter/start}}}} 10 | -------------------------------------------------------------------------------- /hoplife/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.hoplife/init}} 8 | :devtools {:before-load demo.hoplife/stop 9 | :after-load demo.hoplife/start}}}} 10 | -------------------------------------------------------------------------------- /plotSVG/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.plot-svg/init}} 8 | :devtools {:before-load demo.plot-svg/stop 9 | :after-load demo.plot-svg/start}}}} 10 | -------------------------------------------------------------------------------- /todoFRP/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.todo-frp/init}} 8 | :devtools {:before-load demo.todo-frp/stop 9 | :after-load demo.todo-frp/start}}}} 10 | -------------------------------------------------------------------------------- /tictactoe/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.tictactoe/init}} 8 | :devtools {:before-load demo.tictactoe/stop 9 | :after-load demo.tictactoe/start}}}} 10 | -------------------------------------------------------------------------------- /demos-homepage/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.homepage/init}} 8 | :devtools {:before-load demo.homepage/stop 9 | :after-load demo.homepage/start}}}} 10 | -------------------------------------------------------------------------------- /async-webinar/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.async-webinar/init}} 8 | :devtools {:before-load demo.async-webinar/stop 9 | :after-load demo.async-webinar/start}}}} 10 | -------------------------------------------------------------------------------- /validated-form/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.validated-form/init}} 8 | :devtools {:before-load demo.validated-form/stop 9 | :after-load demo.validated-form/start}}}} 10 | -------------------------------------------------------------------------------- /demos-homepage/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hoplon • Demos 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /infinite-scroll/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.infinite-scroll/init}} 8 | :devtools {:before-load demo.infinite-scroll/stop 9 | :after-load demo.infinite-scroll/start}}}} 10 | -------------------------------------------------------------------------------- /lens/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [aatree/aautil "0.0.4"] 5 | [binaryage/devtools "1.0.7"]] 6 | :dev-http {8000 "public"} 7 | :builds {:app {:target :browser 8 | :modules {:main {:init-fn demo.lens/init}} 9 | :devtools {:before-load demo.lens/stop 10 | :after-load demo.lens/start}}}} 11 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.infinite-scroll-paginated/init}} 8 | :devtools {:before-load demo.infinite-scroll-paginated/stop 9 | :after-load demo.infinite-scroll-paginated/start}}}} 10 | -------------------------------------------------------------------------------- /tworker/src/demo/demo_worker.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.demo-worker 2 | (:require-macros 3 | [aaworker.worker-macros :refer [deflpc!]]) 4 | (:require 5 | [aaworker.api :as api])) 6 | 7 | (def clicks (atom 0)) 8 | 9 | (deflpc! click [] 10 | (if (< 2 @clicks) 11 | (throw "too many clicks!") 12 | (do 13 | (if (= 2 @clicks) 14 | (api/send-notice :alert "Enough clicks already!")) 15 | (do 16 | (swap! clicks + 1) 17 | @clicks)))) 18 | 19 | (defn main [] 20 | (api/process-requests)) 21 | -------------------------------------------------------------------------------- /infinite-scroll/public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: monospace; 3 | } 4 | 5 | #container { 6 | margin: 0 auto; 7 | width: 300px; 8 | text-align: center; 9 | } 10 | 11 | #wrapper { 12 | position: relative; 13 | } 14 | 15 | #loading { 16 | padding: 10px; 17 | width: 300px; 18 | position: absolute; 19 | z-index: 1000; 20 | color: white; 21 | background-color: orange; 22 | } 23 | 24 | #scroll { 25 | top: 0px; 26 | width: 100%; 27 | height: 400px; 28 | overflow-y: scroll; 29 | } 30 | -------------------------------------------------------------------------------- /validated-form/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Hoplon • Validated Form 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demos-homepage/README.md: -------------------------------------------------------------------------------- 1 | # demos-homepage 2 | 3 | This project is a page with links for [Hoplon][1] demos 4 | 5 | [See this page.][3] 6 | 7 | ## Dependencies 8 | 9 | - java 1.8+ 10 | - npm 11 | 12 | ## Setup 13 | 14 | Run: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Start the auto-compiler. In a terminal: 23 | 24 | ```bash 25 | $ npx shadow-cljs watch app 26 | ``` 27 | 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/ 34 | -------------------------------------------------------------------------------- /tictactoe/README.md: -------------------------------------------------------------------------------- 1 | # hoplon-tictactoe 2 | 3 | This project demonstrates Tic Tac Toe in [Hoplon][1]. 4 | 5 | [See this demo.][3] 6 | 7 | ## Dependencies 8 | 9 | - java 1.8+ 10 | - npm 11 | 12 | ## Setup 13 | 14 | Run: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Start the auto-compiler. In a terminal: 23 | 24 | ```bash 25 | $ npx shadow-cljs watch app 26 | ``` 27 | 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/tictactoe/ 34 | -------------------------------------------------------------------------------- /counters/README.md: -------------------------------------------------------------------------------- 1 | # counters 2 | 3 | This project demonstrates a simple counting widget in [Hoplon][1]. 4 | 5 | [See this demo.][3] 6 | 7 | ## Dependencies 8 | 9 | - java 1.8+ 10 | - npm 11 | 12 | ## Setup 13 | 14 | Run: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Start the auto-compiler. In a terminal: 23 | 24 | ```bash 25 | $ npx shadow-cljs watch app 26 | ``` 27 | 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/counters/ 34 | -------------------------------------------------------------------------------- /tworker/README.md: -------------------------------------------------------------------------------- 1 | # tworker demo 2 | 3 | This demo exercises [aaworker](https://github.com/aatree/aaworker). 4 | 5 | [See this demo.][3] 6 | 7 | ## Dependencies 8 | 9 | - java 1.8+ 10 | - npm 11 | 12 | ## Setup 13 | 14 | Run: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Start the auto-compiler. In a terminal: 23 | 24 | ```bash 25 | $ npx shadow-cljs watch app 26 | ``` 27 | 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/tworker/ 34 | -------------------------------------------------------------------------------- /validated-form/README.md: -------------------------------------------------------------------------------- 1 | # inputs 2 | 3 | This project demonstrates forms with validations using [Hoplon][1]. 4 | Check out [the demo][3]. 5 | 6 | ## Dependencies 7 | 8 | - java 1.8+ 9 | - npm 10 | 11 | ## Setup 12 | 13 | Run: 14 | 15 | ```bash 16 | $ npm install 17 | ``` 18 | 19 | ## Usage 20 | 21 | 1. Start the auto-compiler. In a terminal: 22 | 23 | ```bash 24 | $ npx shadow-cljs watch app 25 | ``` 26 | 27 | 28 | 2. Go to [http://localhost:8000][2] in your browser. 29 | 30 | [1]: https://hoplon.io 31 | [2]: http://localhost:8000 32 | [3]: https://hoplon.github.io/demos/validated-form/ 33 | -------------------------------------------------------------------------------- /todoFRP/README.md: -------------------------------------------------------------------------------- 1 | # TodoFRP 2 | 3 | An implementation of [TodoMVC][4] using [Hoplon][1]. 4 | 5 | ## Demo 6 | 7 | [View the demo here][3]. 8 | 9 | ## Dependencies 10 | 11 | - java 1.8+ 12 | - npm 13 | 14 | ## Setup 15 | 16 | Run: 17 | 18 | ```bash 19 | $ npm install 20 | ``` 21 | 22 | ## Usage 23 | 24 | 1. Start the auto-compiler. In a terminal: 25 | 26 | ```bash 27 | $ npx shadow-cljs watch app 28 | ``` 29 | 30 | 31 | 2. Go to [http://localhost:8000][2] in your browser. 32 | 33 | [1]: https://hoplon.io 34 | [2]: http://localhost:8000 35 | [3]: https://hoplon.github.io/demos/todoFRP/ 36 | [4]: http://todomvc.com 37 | -------------------------------------------------------------------------------- /inputs/README.md: -------------------------------------------------------------------------------- 1 | # inputs 2 | 3 | This project demonstrates inputs that receive their values from cells and 4 | update the cells on change in [Hoplon][1]. 5 | 6 | [View the demo here][3]. 7 | 8 | ## Dependencies 9 | 10 | - java 1.8+ 11 | - npm 12 | 13 | ## Setup 14 | 15 | Run: 16 | 17 | ```bash 18 | $ npm install 19 | ``` 20 | 21 | ## Usage 22 | 23 | 1. Start the auto-compiler. In a terminal: 24 | 25 | ```bash 26 | $ npx shadow-cljs watch app 27 | ``` 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: http://hoplon.github.io/demos/inputs 34 | -------------------------------------------------------------------------------- /lens/README.md: -------------------------------------------------------------------------------- 1 | # lens demo 2 | 3 | This project exercises dewdrop in the [aautil repository][4] 4 | and integrates dewdrop with [Hoplon][1]. 5 | 6 | [See this demo.][3] 7 | 8 | ## Dependencies 9 | 10 | - java 1.8+ 11 | - npm 12 | 13 | ## Setup 14 | 15 | Run: 16 | 17 | ```bash 18 | $ npm install 19 | ``` 20 | 21 | ## Usage 22 | 23 | 1. Start the auto-compiler. In a terminal: 24 | 25 | ```bash 26 | $ npx shadow-cljs watch app 27 | ``` 28 | 29 | 30 | 2. Go to [http://localhost:8000][2] in your browser. 31 | 32 | [1]: https://hoplon.io 33 | [2]: http://localhost:8000 34 | [3]: https://hoplon.github.io/demos/lens/ 35 | [4]: https://github.com/aatree/aautil#dewdrop 36 | -------------------------------------------------------------------------------- /plotSVG/README.md: -------------------------------------------------------------------------------- 1 | # plotSVG 2 | 3 | A [Hoplon][1] demo that plots some data using SVG. 4 | 5 | ![example plot][4] 6 | 7 | [Demo][3] 8 | 9 | ## Dependencies 10 | 11 | - java 1.8+ 12 | - npm 13 | 14 | ## Setup 15 | 16 | Run: 17 | 18 | ```bash 19 | $ npm install 20 | ``` 21 | 22 | ## Usage 23 | 24 | 1. Start the auto-compiler. In a terminal: 25 | 26 | ```bash 27 | $ npx shadow-cljs watch app 28 | ``` 29 | 30 | 31 | 2. Go to [http://localhost:8000][2] in your browser. 32 | 33 | [1]: https://hoplon.io 34 | [2]: http://localhost:8000 35 | [3]: https://hoplon.github.io/demos/plotSVG/ 36 | [4]: https://github.com/hoplon/demos/blob/master/plotSVG/img/plot.png?raw=true 37 | -------------------------------------------------------------------------------- /w3c-worker/README.md: -------------------------------------------------------------------------------- 1 | # w3c-worker demo 2 | 3 | This project is an adoption of the [w3c schools webworker example][4] in [Hoplon][1]. 4 | 5 | [See this demo.][3] 6 | 7 | ## Dependencies 8 | 9 | - java 1.8+ 10 | - npm 11 | 12 | ## Setup 13 | 14 | Run: 15 | 16 | ```bash 17 | $ npm install 18 | ``` 19 | 20 | ## Usage 21 | 22 | 1. Start the auto-compiler. In a terminal: 23 | 24 | ```bash 25 | $ npx shadow-cljs watch app 26 | ``` 27 | 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/w3c-worker/ 34 | [4]: http://www.w3schools.com/html/html5_webworkers.asp 35 | -------------------------------------------------------------------------------- /contacts/README.md: -------------------------------------------------------------------------------- 1 | # Contacts 2 | 3 | This project demonstrates a simple contact list application on [Hoplon][1] inspired 4 | by the [Om Basic Tutorial](https://github.com/swannodette/om/wiki/Basic-Tutorial). 5 | 6 | Check out [the demo][3]. 7 | 8 | ## Dependencies 9 | 10 | - java 1.8+ 11 | - npm 12 | 13 | ## Setup 14 | 15 | Run: 16 | 17 | ```bash 18 | $ npm install 19 | ``` 20 | 21 | ## Usage 22 | 23 | 1. Start the auto-compiler. In a terminal: 24 | 25 | ```bash 26 | $ npx shadow-cljs watch app 27 | ``` 28 | 29 | 2. Go to [http://localhost:8000][2] in your browser. 30 | 31 | [1]: https://hoplon.io 32 | [2]: http://localhost:8000 33 | [3]: https://hoplon.github.io/demos/contacts/ 34 | -------------------------------------------------------------------------------- /w3c-worker/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"]] 5 | :dev-http {8000 "public"} 6 | :builds {:app {:target :browser 7 | :modules {:main {:init-fn demo.w3c-worker/init 8 | :depends-on #{:shared}} 9 | :shared {:entries []} 10 | :worker {:init-fn counter-worker.counts/main 11 | :depends-on #{:shared} 12 | :web-worker true}} 13 | :devtools {:before-load demo.w3c-worker/stop 14 | :after-load demo.w3c-worker/start}}}} 15 | -------------------------------------------------------------------------------- /tworker/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [binaryage/devtools "1.0.7"] 5 | [aatree/aaworker "0.1.0"]] 6 | :dev-http {8000 "public"} 7 | :builds {:app {:target :browser 8 | :modules {:main {:init-fn demo.tworker/init 9 | :depends-on #{:shared}} 10 | :shared {:entries []} 11 | :worker {:init-fn demo.demo-worker/main 12 | :depends-on #{:shared} 13 | :web-worker true}} 14 | :devtools {:before-load demo.tworker/stop 15 | :after-load demo.tworker/start}}}} 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hoplon Demos 2 | 3 | [Hoplon](https://hoplon.io) is a set of tools 4 | and libraries for making web application front ends. 5 | 6 | This repository contains example Hoplon projects. 7 | 8 | [Check the demos out](https://hoplon.github.io/demos/). 9 | 10 | ## License 11 | 12 | Copyright (c) Alan Dipert and Micha Niskin. All rights 13 | reserved. The use and distribution terms for this software are 14 | covered by the Eclipse Public License 1.0 15 | (http://opensource.org/licenses/eclipse-1.0.php) which can be 16 | found in the file epl-v10.html at the root of this 17 | distribution. By using this software in any fashion, you are 18 | agreeing to be bound by the terms of this license. You must not 19 | remove this notice, or any other, from this software. 20 | -------------------------------------------------------------------------------- /hoplife/README.md: -------------------------------------------------------------------------------- 1 | # hoplife 2 | 3 | This project demonstrates [Conway's Game of Life](http://en.wikipedia.org/wiki/Conway's_Game_of_Life) in [Hoplon][1]. 4 | 5 | Christophe Grand's [extremely elegant and terse method](http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/) is used to implement the game rules. 6 | 7 | See [this demo][3]. 8 | 9 | ## Dependencies 10 | 11 | - java 1.8+ 12 | - npm 13 | 14 | ## Setup 15 | 16 | Run: 17 | 18 | ```bash 19 | $ npm install 20 | ``` 21 | 22 | ## Usage 23 | 24 | 1. Start the auto-compiler. In a terminal: 25 | 26 | ```bash 27 | $ npx shadow-cljs watch app 28 | ``` 29 | 30 | 2. Go to [http://localhost:8000][2] in your browser. 31 | 32 | [1]: https://hoplon.io 33 | [2]: http://localhost:8000 34 | [3]: https://hoplon.github.io/demos/hoplife/ 35 | -------------------------------------------------------------------------------- /tictactoe/public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | color: #333; 4 | } 5 | 6 | pre { 7 | padding: 10px; 8 | background-color: black; 9 | color: chartreuse; 10 | } 11 | 12 | table.tictac { 13 | border-collapse: collapse; 14 | border: 5px solid black; 15 | margin-right: 20px; 16 | } 17 | 18 | table.score { 19 | border-collapse: collapse; 20 | padding-left: 20px; 21 | } 22 | 23 | table.score th { 24 | padding: 5px; 25 | border-bottom: 2px solid black; 26 | } 27 | 28 | table.score td { 29 | padding: 5px; 30 | } 31 | 32 | table.tictac td { 33 | border: 5px solid black; 34 | cursor: pointer; 35 | width: 1.3em; 36 | height: 1.3em; 37 | text-align: center; 38 | vertical-align: middle; 39 | font: bold 36px sans-serif 40 | } -------------------------------------------------------------------------------- /infinite-scroll/README.md: -------------------------------------------------------------------------------- 1 | # Hoplon • Infinite Scroll Demo 2 | 3 | An infinitely scrollable container built using [Hoplon][1]. Keep scrolling 4 | to see more images; when you reach the bottom it'll fetch more images and 5 | append them so you can continue scrolling and seeing more images...forever. 6 | 7 | ## Demo 8 | 9 | [View the demo here][3]. 10 | 11 | ## Dependencies 12 | 13 | - java 1.8+ 14 | - npm 15 | 16 | ## Setup 17 | 18 | Run: 19 | 20 | ```bash 21 | $ npm install 22 | ``` 23 | 24 | ## Usage 25 | 26 | 1. Start the auto-compiler. In a terminal: 27 | 28 | ```bash 29 | $ npx shadow-cljs watch app 30 | ``` 31 | 32 | 33 | 2. Go to [http://localhost:8000][2] in your browser. 34 | 35 | [1]: https://hoplon.io 36 | [2]: http://localhost:8000 37 | [3]: http://hoplon.github.io/demos/infinite-scroll 38 | -------------------------------------------------------------------------------- /duracell/shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | ;; shadow-cljs configuration 2 | {:source-paths ["src"] 3 | :dependencies [[hoplon/hoplon "7.3.5"] 4 | [aatree/durable-cells "0.1.0"] 5 | [binaryage/devtools "1.0.7"]] 6 | :dev-http {8000 "public"} 7 | :builds {:app {:target :browser 8 | :output-dir "public" 9 | :asset-path "/" 10 | :modules {:main {:init-fn demo.duracell/init 11 | :depends-on #{:shared}} 12 | :shared {:entries []} 13 | :dcells {:init-fn durable-cells.dc-api/start 14 | :depends-on #{:shared} 15 | :web-worker true}} 16 | :devtools {:before-load demo.duracell/stop 17 | :after-load demo.duracell/start}}}} 18 | -------------------------------------------------------------------------------- /duracell/README.md: -------------------------------------------------------------------------------- 1 | # duracell demo 2 | 3 | This is a demo showing how easy it is to use 4 | [IndexedDb](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) 5 | running in a 6 | [web worker](http://www.w3schools.com/html/html5_webworkers.asp). 7 | 8 | The duracell demo builds on [Hoplon](https://hoplon.io), 9 | [aaworker](https://github.com/aatree/aaworker) 10 | and [durable-cells](https://github.com/aatree/durable-cells). 11 | 12 | [See this demo.](https://hoplon.github.io/demos/duracell/) 13 | 14 | ## Dependencies 15 | 16 | - java 1.8+ 17 | - npm 18 | 19 | ## Setup 20 | 21 | Run: 22 | 23 | ```bash 24 | $ npm install 25 | ``` 26 | 27 | ## Usage 28 | 29 | 1. Start the auto-compiler. In a terminal: 30 | 31 | ```bash 32 | $ npx shadow-cljs watch app 33 | ``` 34 | 35 | 36 | 2. Go to [http://localhost:8000](http://localhost:8000) in your browser. 37 | -------------------------------------------------------------------------------- /tictactoe/src/demo/tictactoe/advanced.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.tictactoe.advanced 2 | (:require 3 | [hoplon.core :as h] 4 | [demo.tictactoe.element :as tictactoe] 5 | [javelin.core :refer [cell cell= defc defc= dosync]])) 6 | 7 | (defc winners nil) 8 | 9 | (h/defelem board 10 | [_ [page]] 11 | (h/div 12 | (h/h2 "Small Board") 13 | (tictactoe/game {:size "3"}) 14 | (h/h2 "Big Board") 15 | (tictactoe/game {:size "5"}) 16 | (h/h2 "Wire Up A Scoreboard") 17 | (tictactoe/game {:size "3" :history winners :style "float:left"}) 18 | (tictactoe/scoreboard {:history winners}) 19 | (h/div {:style "clear:both"} 20 | (h/div {:style "padding-top: 20px;padding-bottom:50px;"} 21 | (h/a {:href "https://github.com/hoplon/demos/blob/master/tictactoe/src/demo/tictactoe/advanced.cljs"} 22 | "Source Code") 23 | " " 24 | (h/a {:href "#" :click #(reset! page :index)} "Index"))))) 25 | -------------------------------------------------------------------------------- /w3c-worker/src/demo/w3c_worker.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.w3c-worker 2 | (:require 3 | [clojure.string :as str] 4 | [hoplon.core :as h] 5 | [hoplon.goog] 6 | [javelin.core :refer [cell cell= defc defc= dosync]])) 7 | 8 | (defc counter nil) 9 | (def w (atom nil)) 10 | 11 | (defn start-worker [] 12 | (if (not @w) 13 | (reset! w (js/Worker. "js/worker.js")) 14 | (println "already exists!")) 15 | (set! (.-onmessage @w) #(reset! counter (.-data %)))) 16 | 17 | (defn stop-worker [] 18 | (when @w 19 | (.terminate @w) 20 | (reset! w nil))) 21 | 22 | (h/defelem w3c-worker [] 23 | (h/div 24 | (h/p (h/text "Count numbers: ~{counter}")) 25 | (h/button :click start-worker "Start worker") 26 | (h/button :click stop-worker "Stop worker"))) 27 | 28 | (defn mount-components [] 29 | (.replaceChildren (.getElementById js/document "app") 30 | (w3c-worker))) 31 | 32 | (defn start [] 33 | (mount-components) 34 | (js/console.log "Starting...")) 35 | 36 | (defn stop [] 37 | (js/console.log "Stopping...")) 38 | 39 | (defn init [] 40 | (js/console.log "Initializing...") 41 | (start)) 42 | -------------------------------------------------------------------------------- /tworker/src/demo/tworker.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.tworker 2 | (:require 3 | [aaworker.lpc :as lpc] 4 | [hoplon.core :as h] 5 | [hoplon.jquery] 6 | [javelin.core :refer [cell cell= defc defc= dosync]])) 7 | 8 | (defc state 0) 9 | (defc error nil) 10 | (defc loading nil) 11 | 12 | (def clear-error! #(reset! error nil)) 13 | (defc= loading? (seq loading)) 14 | 15 | (lpc/new-worker! "js/worker.js") 16 | (def click (lpc/mklocal! 'click "js/worker.js" state error loading)) 17 | 18 | (defn click-it [] (click)) 19 | 20 | (h/defelem tworker [] 21 | (h/div 22 | (h/div 23 | :id "error" 24 | :click clear-error! 25 | :slide-toggle error 26 | :css {:display "none"} 27 | (h/text "~{error}")) 28 | (h/p state 29 | (h/text " ") 30 | (h/button :click click-it "Click!")))) 31 | 32 | (defn mount-components [] 33 | (.replaceChildren (.getElementById js/document "app") 34 | (tworker))) 35 | 36 | (defn start [] 37 | (mount-components) 38 | (js/console.log "Starting...")) 39 | 40 | (defn stop [] 41 | (js/console.log "Stopping...")) 42 | 43 | (defn init [] 44 | (js/console.log "Initializing...") 45 | (start)) 46 | -------------------------------------------------------------------------------- /async-webinar/README.md: -------------------------------------------------------------------------------- 1 | # async-webinar 2 | 3 | A [Hoplon][1] demo that performs the examples from [Designing Front End Applications with core.async][4] 4 | Cognitect's webinar. The original code can be found [here][5]. 5 | 6 | Examples 4 & 5 were not reproduced as they seemed explicitly core.async-centric. Also, I believe 7 | that example 6 exposes a bug in the original, wherein if you click the B button before the A button, 8 | the click is queued and immediately triggers the next message. It seems that the proper behavior should 9 | be to discard clicks to B until the A button has been clicked and then wait for a click to B. 10 | 11 | [See this demo.][3] 12 | 13 | ## Dependencies 14 | 15 | - java 1.8+ 16 | - npm 17 | 18 | ## Setup 19 | 20 | Run: 21 | 22 | ```bash 23 | $ npm install 24 | ``` 25 | 26 | ## Usage 27 | 28 | 1. Start the auto-compiler. In a terminal: 29 | 30 | ```bash 31 | $ npx shadow-cljs watch app 32 | ``` 33 | 34 | 35 | 2. Go to [http://localhost:8000][2] in your browser. 36 | 37 | [1]: https://hoplon.io 38 | [2]: http://localhost:8000 39 | [3]: https://hoplon.github.io/demos/async-webinar/ 40 | [4]: http://go.cognitect.com/core_async_webinar_recording 41 | [5]: https://github.com/cognitect/async-webinar 42 | -------------------------------------------------------------------------------- /demos-homepage/public/css/main.css: -------------------------------------------------------------------------------- 1 | @import url('http://fonts.googleapis.com/css?family=Bree+Serif'); 2 | 3 | .container h1, 4 | .container h2, 5 | .container h3, 6 | .container h4, 7 | .container h5, 8 | .container h6 { 9 | /* font-family: 'Bree Serif', serif; */ 10 | /* font-family: 'Lilita One', serif; */ 11 | font-family: 'Bree Serif', serif; 12 | } 13 | 14 | div.jumbotron { 15 | padding: 0px; 16 | background: #ffcc33; 17 | background: -moz-linear-gradient(45deg, #ffcc33 0%, #990000 100%); 18 | background: -webkit-gradient(linear, left bottom, right top, color-stop(0%,#020031), color-stop(100%,#990000)); 19 | background: -webkit-linear-gradient(45deg, #ffcc33 0%,#990000 100%); 20 | background: -o-linear-gradient(45deg, #ffcc33 0%,#990000 100%); 21 | background: -ms-linear-gradient(45deg, #ffcc33 0%,#990000 100%); 22 | background: linear-gradient(45deg, #ffcc33 0%,#990000 100%); 23 | filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffcc33', endColorstr='#990000',GradientType=1 ); 24 | width: 100%; 25 | } 26 | 27 | .lambda { 28 | background-image: url(../image/Lambda_lc.svg); 29 | background-repeat: no-repeat; 30 | background-position: right; 31 | padding:48px; 32 | height: 250px; 33 | } 34 | -------------------------------------------------------------------------------- /duracell/src/demo/duracell.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.duracell 2 | (:require 3 | [durable-cells.core :refer [open-durable-cells! error ready]] 4 | [hoplon.core :as h] 5 | [hoplon.jquery] 6 | [javelin.core :refer [cell cell= dosync defc]])) 7 | 8 | (set! cljs.core/*print-fn* #(.log js/console %)) 9 | 10 | (def clear-error! #(reset! error nil)) 11 | (defc txt nil) 12 | 13 | (open-durable-cells! {"txt" txt}) 14 | 15 | (h/defelem duracell [] 16 | (h/div 17 | :css {:display "none"} 18 | :toggle ready 19 | (h/div 20 | :id "error" 21 | :click clear-error! 22 | :slide-toggle error 23 | :css {:display "none"} 24 | (h/text "~{error}")) 25 | (h/h2 (h/text "IndexedDB Demo: Duracell")) 26 | (h/p (h/input 27 | :type "text" 28 | :value txt 29 | :keyup #(reset! txt @%))) 30 | (h/p (h/text "Type something and then refresh the page--nothing is lost.")))) 31 | 32 | (defn mount-components [] 33 | (.replaceChildren (.getElementById js/document "app") 34 | (duracell))) 35 | 36 | (defn start [] 37 | (mount-components) 38 | (js/console.log "Starting...")) 39 | 40 | (defn stop [] 41 | (js/console.log "Stopping...")) 42 | 43 | (defn init [] 44 | (js/console.log "Initializing...") 45 | (start)) 46 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/README.md: -------------------------------------------------------------------------------- 1 | # Hoplon • Infinite Scroll Demo with pagination 2 | 3 | A potentially infinitely scrollable container built using [Hoplon][1]. 4 | Keep scrolling to see more numbers divided in pages; when you almost reach 5 | the bottom it'll fetch more pages full of numbers and append them so you can 6 | continue scrolling and seeing more numbers... until page 99. 7 | 8 | The address bar will change to show in witch page you are. If you type an 9 | already loaded page on address bar it will scroll to that page. If you 10 | type a page that isn't loaded the browser will refresh and take you there. 11 | If didn't start on the first page you can scroll up too. 12 | 13 | It's not really connecting to a server, all the data is fake. 14 | 15 | ## Demo 16 | 17 | [View the demo here][3]. 18 | 19 | ## Dependencies 20 | 21 | - java 1.8+ 22 | - npm 23 | 24 | ## Setup 25 | 26 | Run: 27 | 28 | ```bash 29 | $ npm install 30 | ``` 31 | 32 | ## Usage 33 | 34 | 1. Start the auto-compiler. In a terminal: 35 | 36 | ```bash 37 | $ npx shadow-cljs watch app 38 | ``` 39 | 40 | 41 | 2. Go to [http://localhost:8000][2] in your browser. 42 | 43 | [1]: https://hoplon.io 44 | [2]: http://localhost:8000 45 | [3]: http://hoplon.github.io/demos/infinite-scroll-paginated 46 | -------------------------------------------------------------------------------- /lens/src/demo/lens.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.lens 2 | (:require 3 | [hoplon.core :as h] 4 | [aautil.dewdrop :as lens] 5 | [hoplon.goog] 6 | [javelin.core :refer [cell cell= defc defc= dosync]])) 7 | 8 | (set! cljs.core/*print-fn* #(.log js/console %)) 9 | 10 | (defc composite-data {"A" "Apple" "B" "Boy"}) 11 | (defc lens-key "A") 12 | 13 | (def keyed-lens (lens/atom-key-lens lens-key)) 14 | (def keyed-view (lens/lview keyed-lens composite-data)) 15 | 16 | (defn revalue [_ _] @keyed-view) 17 | 18 | (defc= value 19 | (revalue lens-key composite-data) 20 | (fn [item] (reset! keyed-view item))) 21 | 22 | (h/defelem lens 23 | [] 24 | (h/div 25 | (h/p (h/text "data: ~{composite-data}")) 26 | (h/p (h/text "key: ") 27 | (h/input 28 | :type "text" 29 | :value lens-key 30 | :keyup #(reset! lens-key @%))) 31 | (h/p (h/text "valuesss: ") 32 | (h/input 33 | :type "text" 34 | :value value 35 | :keyup #(reset! value @%))) 36 | (h/p (h/a :href "https://github.com/hoplon/demos/tree/master/lens" "Source code")))) 37 | 38 | (defn mount-components [] 39 | (.replaceChildren (.getElementById js/document "app") 40 | (lens))) 41 | 42 | (defn start [] 43 | (mount-components) 44 | (js/console.log "Starting...")) 45 | 46 | (defn stop [] 47 | (js/console.log "Stopping...")) 48 | 49 | (defn init [] 50 | (js/console.log "Initializing...") 51 | (start)) 52 | -------------------------------------------------------------------------------- /async-webinar/public/css/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0 0 300px 0; 3 | padding: 0; 4 | font-family: helvetica, arial, sans-serif; 5 | } 6 | 7 | button { 8 | outline: none; 9 | color: #666; 10 | font-size: 16px; 11 | border: 2px solid #666; 12 | background-color: white; 13 | border-radius: 4px; 14 | padding: 4px 12px; 15 | text-transform: uppercase; 16 | } 17 | 18 | button:hover { 19 | color: black; 20 | border-color: black; 21 | } 22 | 23 | button.disabled:hover { 24 | color: #666; 25 | border-color: #666; 26 | } 27 | 28 | button:active { 29 | background-color: #efefef; 30 | } 31 | 32 | button.disabled:active { 33 | background-color: white; 34 | } 35 | 36 | table td.left { 37 | vertical-align: top; 38 | } 39 | 40 | .example { 41 | padding: 20px; 42 | border-bottom: 1px solid #333; 43 | } 44 | 45 | .example table { 46 | width: 100%; 47 | } 48 | 49 | .example table td { 50 | width: 50%; 51 | } 52 | 53 | h2 { 54 | margin-top: 0px; 55 | } 56 | 57 | .display { 58 | background-color: #efefef; 59 | padding: 10px; 60 | font-family: courier, monospace; 61 | font-size: 16px; 62 | border: 1px solid #ccc; 63 | } 64 | 65 | .scrolling { 66 | position: relative; 67 | height: 220px; 68 | max-height: 200px; 69 | overflow-y: hidden; 70 | } 71 | 72 | .scrolling > div { 73 | position: absolute; 74 | bottom: -12px; 75 | left: 0px; 76 | right: 0px; 77 | } 78 | 79 | button.disabled { 80 | opacity: 0.5; 81 | } -------------------------------------------------------------------------------- /tictactoe/src/demo/tictactoe.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.tictactoe 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.goog] 5 | [demo.tictactoe.advanced :as advanced] 6 | [demo.tictactoe.basic :as basic] 7 | [javelin.core :refer [cell cell= dosync]])) 8 | 9 | (defonce page (cell :index)) 10 | 11 | (h/defelem index 12 | [] 13 | (h/div 14 | (h/h1 "Tic Tac Toe in Hoplon") 15 | (h/p 16 | (h/a {:href "https://hoplon.io"} "Hoplon") 17 | " is a set of libraries and tools for creating dynamic web applications.") 18 | (h/p "This project demonstrates a Hoplon Tic Tac Toe in two ways:") 19 | (h/ol 20 | (h/li 21 | (h/a {:href "#" :click #(reset! page :basic)} "basic.cljs") 22 | " - a simple game of Tic Tac Toe.") 23 | (h/li 24 | (h/a {:href "#" :click #(reset! page :advanced)} "advanced.cljs") 25 | " - games of Tic Tac Toe with different sizes and score")) 26 | (h/p 27 | (h/a 28 | {:href "https://github.com/hoplon/demos/tree/master/tictactoe"} 29 | "Source Code")))) 30 | 31 | (h/defelem tictactoe [] 32 | (h/div 33 | (h/case-tpl page 34 | :index (index) 35 | :basic (basic/board page) 36 | :advanced (advanced/board page) 37 | (h/a {:href "#" :click #(reset! page :index)} "Index")))) 38 | 39 | (defn mount-components [] 40 | (.replaceChildren (.getElementById js/document "app") 41 | (tictactoe))) 42 | 43 | (defn start [] 44 | (mount-components) 45 | (js/console.log "Starting...")) 46 | 47 | (defn stop [] 48 | (js/console.log "Stopping...")) 49 | 50 | (defn init [] 51 | (js/console.log "Initializing...") 52 | (start)) 53 | -------------------------------------------------------------------------------- /counters/src/demo/counter.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.counter 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.goog] 5 | [javelin.core :as j])) 6 | 7 | (h/defelem swap-button 8 | [{:keys [state func] :or {func identity} :as attr} kids] 9 | (let [attr (dissoc attr :state :func)] 10 | (h/button (assoc attr :click #(swap! state func)) 11 | kids))) 12 | 13 | (h/defelem counter 14 | [attr _] 15 | (let [my-count (j/cell 0)] 16 | (h/div attr 17 | (h/label (h/text "~{my-count}")) 18 | (swap-button :state my-count :func inc "+") 19 | (swap-button :state my-count :func dec "-")))) 20 | 21 | (h/defelem counters 22 | [{:keys [size] :or {size 10} :as attr} _] 23 | (let [last-clicked (j/cell nil) 24 | attr (dissoc attr :size)] 25 | (h/div attr 26 | (h/h1 "A Counting Widget!") 27 | (h/p {:toggle last-clicked} 28 | (h/text "Last clicked item was ~{last-clicked}")) 29 | (h/loop-tpl :bindings [i (j/cell= (range 0 size))] 30 | (counter :click #(reset! last-clicked @i)))))) 31 | 32 | (defn mount-components [] 33 | (.replaceChildren (.getElementById js/document "app") 34 | (counters :size 10) 35 | (h/p (h/a :href "https://github.com/hoplon/demos/blob/master/counters/src/demo/counter.cljs" "Source code")) 36 | (h/p "Inspired by " (h/a :href "https://github.com/swannodette/om/tree/master/examples/counters" "the Om demo of the same name.")))) 37 | 38 | (defn start [] 39 | (mount-components) 40 | (js/console.log "Starting...")) 41 | 42 | (defn stop [] 43 | (js/console.log "Stopping...")) 44 | 45 | (defn init [] 46 | (js/console.log "Initializing...") 47 | (start)) 48 | -------------------------------------------------------------------------------- /hoplife/src/demo/hoplife.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.hoplife 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.goog] 5 | [javelin.core :refer [cell cell= defc]])) 6 | 7 | (def default #{[2 1] [2 2] [0 1] [1 2] [2 0] [13 11] [12 11] [11 11]}) 8 | (def +size+ 16) 9 | (def +interval+ 100) 10 | (defc running? true) 11 | (defc alive default) 12 | 13 | ;; See http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/ 14 | ;; Slightly modified to be toroidal 15 | (defn neighbours [[x y]] 16 | (for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])] 17 | [(mod (+ dx x) +size+) (mod (+ dy y) +size+)])) 18 | 19 | (defn step [cells] 20 | (set (for [[loc n] (frequencies (mapcat neighbours cells)) 21 | :when (or (= n 3) (and (= n 2) (cells loc)))] 22 | loc))) 23 | 24 | (defn click [cells xy] 25 | ((if (contains? cells xy) disj conj) cells xy)) 26 | 27 | (h/defelem hoplife [] 28 | (h/div 29 | (h/h2 "Hoplife ") 30 | (h/button :click #(swap! running? not) :text (cell= (if running? "Stop" "Start"))) 31 | (h/button :click #(swap! alive into default) "Reset") 32 | (h/table 33 | (for [x (range +size+)] 34 | (h/tr (for [y (range +size+)] 35 | (h/td :click #(swap! alive click [x y]) 36 | :class (cell= {"alive" (contains? alive [x y])})))))) 37 | (h/p (h/a :href "https://github.com/hoplon/demos/tree/master/hoplife" "Source Code")))) 38 | 39 | (defn mount-components [] 40 | (.replaceChildren (.getElementById js/document "app") 41 | (hoplife))) 42 | 43 | (defn start [] 44 | (mount-components) 45 | (js/console.log "Starting...")) 46 | 47 | (defn stop [] 48 | (js/console.log "Stopping...")) 49 | 50 | (defn init [] 51 | (js/console.log "Initializing...") 52 | (start) 53 | (h/with-init! 54 | (h/with-interval +interval+ 55 | (when @running? 56 | (swap! alive step))))) 57 | -------------------------------------------------------------------------------- /demos-homepage/src/demo/homepage.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.homepage 2 | (:require-macros 3 | [macros.core :refer [demo-dirs]]) 4 | (:require 5 | [hoplon.twitter.bootstrap :refer [container]] 6 | [clojure.string :as str] 7 | [hoplon.core :as h] 8 | [hoplon.goog] 9 | [javelin.core :refer [cell cell= dosync]])) 10 | 11 | (def url:hoplon "https://hoplon.io") 12 | (def url:hoplon-demos "https://github.com/hoplon/demos") 13 | (def url:hoplon-slack "https://clojurians.slack.com/messages/hoplon/") 14 | (def url:clojurians "http://clojurians.net/") 15 | 16 | (h/defelem demo-item [_ [name]] 17 | (h/li :class "list-group-item" 18 | (h/a :href name name))) 19 | 20 | (h/defelem copyright-footer [_ _] 21 | (h/div :css {:padding "30px 0px"} 22 | (h/div :css {:text-align "center"} 23 | "Copyright © Alan Dipert and Micha Niskin. All rights reserved."))) 24 | 25 | (h/defelem homepage [] 26 | (h/div 27 | (h/div :class "jumbotron" 28 | (container :class "lambda" 29 | (h/h1 "Hoplon Demos") 30 | (h/p "A simpler way to program the web, with examples!"))) 31 | (container 32 | (h/div :col {:sm 6} 33 | (h/p "Check out the demos, then try these links:") 34 | (h/ul 35 | (h/li "The " (h/a :href url:hoplon "Hoplon website.")) 36 | (h/li "Source code for all demos is " (h/a :href url:hoplon-demos "here.")) 37 | (h/li 38 | "Join the " (h/a :href url:hoplon-slack "Hoplon Slack channel.") " " 39 | "Grab an invite to Slack " (h/a :href url:clojurians "here") " if you are not registered."))) 40 | (h/div :col {:sm 6} 41 | (h/ul :class "list-group" 42 | (mapv demo-item (demo-dirs))))) 43 | (h/hr) 44 | (copyright-footer))) 45 | 46 | (defn mount-components [] 47 | (.replaceChildren (.getElementById js/document "app") 48 | (homepage))) 49 | 50 | (defn start [] 51 | (mount-components) 52 | (js/console.log "Starting...")) 53 | 54 | (defn stop [] 55 | (js/console.log "Stopping...")) 56 | 57 | (defn init [] 58 | (js/console.log "Initializing...") 59 | (start)) 60 | -------------------------------------------------------------------------------- /tictactoe/src/demo/tictactoe/basic.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.tictactoe.basic 2 | (:require 3 | [hoplon.core :as h] 4 | [javelin.core :refer [cell cell= defc defc= dosync]])) 5 | 6 | (def transpose (partial apply map vector)) 7 | (def diagonal (partial map (comp first drop) (range))) 8 | (def indexed (partial map-indexed vector)) 9 | (def new-game (vec (repeat 9 nil))) 10 | 11 | (defc game new-game) 12 | (defc undos ()) 13 | (defc= rows (partition 3 game)) 14 | (defc= columns (transpose rows)) 15 | (defc= diagonals [(diagonal rows) (diagonal (map reverse rows))]) 16 | (defc= runs (concat rows columns diagonals)) 17 | (defc= winner (->> runs 18 | (map set) 19 | (remove #(contains? % nil)) 20 | (filter #(= 1 (count %))) 21 | ffirst)) 22 | (defc= moves (->> (indexed game) 23 | (filter (comp nil? second)) 24 | (map first))) 25 | (defc= no-moves? (not (seq moves))) 26 | (defc= over (cond winner (str winner " won!") 27 | no-moves? "Cat's game.")) 28 | 29 | (defn undo! [] 30 | (when (seq @undos) 31 | (reset! game (peek @undos)) 32 | (swap! undos pop))) 33 | 34 | (defn ai! [] 35 | (when-not @over 36 | (swap! game assoc (rand-nth @moves) "O"))) 37 | 38 | (defn play! [i j] 39 | (let [idx (+ (* i 3) j)] 40 | (when (and (not @over) (nil? (get @game idx))) 41 | (swap! undos conj @game) 42 | (swap! game assoc idx "X")))) 43 | 44 | (defn reset-game! [] 45 | (reset! game new-game) 46 | (reset! undos ())) 47 | 48 | (h/defelem board 49 | [_ [page]] 50 | (h/div 51 | (h/table :class "tictac" 52 | (h/loop-tpl :bindings [[i row] (cell= (indexed rows))] 53 | (h/tr 54 | (h/loop-tpl :bindings [[j x] (cell= (indexed row))] 55 | (h/td :click #(and (play! @i @j) (ai!)) (h/text "~{x}")))))) 56 | (h/div :toggle (cell= (and (not over) (seq undos))) 57 | (h/button :click undo! "Undo")) 58 | (h/div :toggle over 59 | (h/p (h/text "~{over}")) 60 | (h/button :click reset-game! "Play Again")) 61 | (h/a :href "https://github.com/hoplon/demos/blob/master/tictactoe/src/demo/tictactoe/basic.cljs" 62 | "Source Code") 63 | " " 64 | (h/a {:href "#" :click #(reset! page :index)} " Index"))) 65 | -------------------------------------------------------------------------------- /plotSVG/src/demo/chart.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.chart 2 | (:require 3 | [hoplon.core :as h] 4 | [javelin.core :refer [cell cell= dosync cell-let]] 5 | [clojure.string :as str] 6 | [hoplon.svg :as svg])) 7 | 8 | (defrecord Chart [width height min-x max-x min-y max-y]) 9 | 10 | (defn config [& {:keys [width height min-x max-x min-y max-y]}] 11 | (Chart. width height min-x max-x min-y max-y)) 12 | 13 | (defn rel-coord [{:keys [width height min-x max-x min-y max-y]} x y] 14 | (let [w (- max-x min-x) 15 | h (- max-y min-y) 16 | dw (- x min-x) 17 | dh (- y min-y)] 18 | [(* width (/ dw w)) (* height (- 1 (/ dh h)))])) 19 | 20 | (h/defelem container [{:keys [chart] :as attr} kids] 21 | (cell-let [{:keys [width height]} chart] 22 | (svg/svg (assoc (dissoc attr :chart) :width width :height height) kids))) 23 | 24 | (h/defelem point-circle [{:keys [chart x y] :as attr} _] 25 | (let [coord (cell= (rel-coord chart x y))] 26 | ((svg/circle 27 | :cx (cell= (first coord)) 28 | :cy (cell= (second coord))) 29 | (dissoc attr :chart :x :y) 30 | (svg/title (h/text "[~{x}, ~{y}]"))))) 31 | 32 | (h/defelem point-rect [{:keys [chart x y width height] :as attr} _] 33 | (let [coord (cell= (rel-coord chart x y))] 34 | ((svg/rect 35 | :x (cell= (- (first coord) (/ width 2))) 36 | :y (cell= (- (second coord) (/ height 2)))) 37 | (dissoc attr :chart :x :y) 38 | (svg/title (h/text "[~{x}, ~{y}]"))))) 39 | 40 | (h/defelem points-rect [{:keys [chart data width height] :as attr} _] 41 | (svg/g 42 | (h/loop-tpl :bindings [[x y] data] 43 | ((point-rect :chart chart :x x :y y :width width :height height) 44 | (dissoc attr :chart :data :width :height))))) 45 | 46 | (h/defelem points-circle [{:keys [chart data] :as attr} _] 47 | (svg/g 48 | (h/loop-tpl :bindings [[x y] data] 49 | ((point-circle :chart chart :x x :y y) (dissoc attr :chart :data))))) 50 | 51 | (h/defelem polygon [{:keys [chart data] :as attr} _] 52 | (let [start (cell= (str "0," (:height chart))) 53 | end (cell= (str (:width chart) "," (:height chart))) 54 | rels (cell= (for [[x y] data] 55 | (let [[x' y'] (rel-coord chart x y)] 56 | (str x' "," y')))) 57 | points (cell= (str start " " (str/join " " rels) " " end))] 58 | ((svg/polygon :points points) (dissoc attr :chart :data)))) 59 | 60 | (h/defelem polyline [{:keys [chart data] :as attr} _] 61 | (let [rels (cell= (for [[x y] data] 62 | (let [[x' y'] (rel-coord chart x y)] 63 | (str x' "," y')))) 64 | points (cell= (str/join " " rels))] 65 | ((svg/polyline :points points) (dissoc attr :chart :data)))) 66 | -------------------------------------------------------------------------------- /infinite-scroll/src/demo/infinite_scroll.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.infinite-scroll 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.goog] 5 | [javelin.core :refer [cell cell= dosync defc defc=]])) 6 | 7 | (defn new-image 8 | [random] 9 | (str "https://picsum.photos/280/200?random=" random)) 10 | 11 | (def random-index (atom -1)) 12 | (defc images "The current list of image urls." []) 13 | (defc loading "A vector of in-progress async calls." []) 14 | (defc= loading? "True if there are in-progress async calls." (seq loading)) 15 | 16 | (defmethod h/on! :scroll-end 17 | ;; Add the :scroll-end attribute, which fires its callback when the element is 18 | ;; scrolled down as far as it can go--to the bottom. 19 | [elem _ callback] 20 | (h/on! elem :scroll 21 | #(let [el (.-target %) 22 | ch (.-clientHeight el) 23 | sh (.-scrollHeight el) 24 | st (.-scrollTop el) 25 | at-end? (= ch (- sh st))] 26 | (when at-end? (callback %))))) 27 | 28 | (defn fetch-images! 29 | "Append more images" 30 | [] 31 | (when-not @loading? 32 | (swap! loading conj :loading) 33 | (h/with-timeout 500 34 | (swap! loading pop) 35 | (swap! images into [(new-image (swap! random-index inc)) 36 | (new-image (swap! random-index inc)) 37 | (new-image (swap! random-index inc)) 38 | (new-image (swap! random-index inc)) 39 | (new-image (swap! random-index inc)) 40 | (new-image (swap! random-index inc)) 41 | (new-image (swap! random-index inc)) 42 | (new-image (swap! random-index inc)) 43 | (new-image (swap! random-index inc)) 44 | (new-image (swap! random-index inc))])))) 45 | 46 | (h/defelem infinite-scroll [] 47 | (h/div :id "container" 48 | (h/h2 "Infinite Scroll ") 49 | (h/p "Scroll down to see more images...") 50 | (h/div :id "wrapper" 51 | (h/div :id "loading" :style "display:none" :toggle loading? "LOADING IMAGES...") 52 | (h/div :id "scroll" :scroll-end fetch-images! 53 | (h/loop-tpl :bindings [image images] 54 | (h/div (h/img :src image))))) 55 | 56 | (h/p (h/a :href "https://github.com/hoplon/demos/tree/master/infinite-scroll" "Source Code")))) 57 | 58 | (defn mount-components [] 59 | (.replaceChildren (.getElementById js/document "app") 60 | (infinite-scroll))) 61 | 62 | (defn start [] 63 | (mount-components) 64 | (fetch-images!) 65 | (js/console.log "Starting...")) 66 | 67 | (defn stop [] 68 | (js/console.log "Stopping...")) 69 | 70 | (defn init [] 71 | (js/console.log "Initializing...") 72 | (start)) 73 | -------------------------------------------------------------------------------- /validated-form/src/demo/validated_form.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.validated-form 2 | (:require-macros [vform.core :refer [defv]]) 3 | (:require 4 | [hoplon.core :as h] 5 | [hoplon.jquery] 6 | [javelin.core :refer [cell cell= defc defc= dosync]])) 7 | 8 | ;; input cells 9 | 10 | (defc form-name nil) 11 | (defc form-email nil) 12 | 13 | ;; helpers / validators 14 | 15 | (defn validate-presence [v] (seq v)) 16 | 17 | (defn validate-regexp [r] 18 | (fn [v] (re-matches r (or v "")))) 19 | 20 | ;; formula cells 21 | 22 | (defv form-name-valid? form-name validate-presence) 23 | (defv form-email-valid? form-email (validate-regexp #"(?i)\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b")) 24 | 25 | (defc= is-form-valid? (and (:valid? form-name-valid?) (:valid? form-email-valid?))) 26 | 27 | (defc= form-data {:name form-name 28 | :email form-email}) 29 | 30 | ;; operations 31 | 32 | (defn submit-form [_] (js/alert (str "Send: " (pr-str @form-data)))) 33 | 34 | ;; custom elements 35 | 36 | (h/defelem cell-input [{:keys [cell] :as attrs}] 37 | (let [target-value #(h/do! (-> % .-currentTarget) :value)] 38 | (h/input (-> attrs 39 | (dissoc :cell) 40 | (assoc :type (:type attrs "text") 41 | :value cell 42 | :input #(reset! cell (target-value %))))))) 43 | 44 | (h/defelem form-group [{:keys [valid?] :as attrs} body] 45 | (let [dirty? (fn [v] (-> v nil? not))] 46 | ((h/div (dissoc attrs :valid?) body) 47 | :class (cell= {:form-group true 48 | :has-error (and (dirty? (:value valid?)) (not (:valid? valid?)))})))) 49 | 50 | (h/defelem input-control [attrs] 51 | ((cell-input attrs) :class {:form-control true})) 52 | 53 | ;; interface 54 | (h/defelem validated-form [] 55 | (h/div :class "site-wrapper" 56 | (h/div :class "site-wrapper-inner" 57 | (h/div :class "cover-container" 58 | (h/div :class "inner cover" 59 | (h/form :submit submit-form 60 | (form-group :valid? form-name-valid? 61 | (h/label "Name") 62 | (input-control :cell form-name)) 63 | (form-group :valid? form-email-valid? 64 | (h/label "Email") 65 | (input-control :cell form-email)) 66 | (h/div :class "text-right" 67 | (h/button :class "btn btn-primary" 68 | :type "submit" 69 | :disabled (cell= (not is-form-valid?)) "Submit")))))))) 70 | 71 | (defn mount-components [] 72 | (.replaceChildren (.getElementById js/document "app") 73 | (validated-form))) 74 | 75 | (defn start [] 76 | (mount-components) 77 | (js/console.log "Starting...")) 78 | 79 | (defn stop [] 80 | (js/console.log "Stopping...")) 81 | 82 | (defn init [] 83 | (js/console.log "Initializing...") 84 | (start)) 85 | -------------------------------------------------------------------------------- /tictactoe/src/demo/tictactoe/element.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.tictactoe.element 2 | (:require 3 | [hoplon.core :as h] 4 | [javelin.core :refer [cell cell= defc defc=]])) 5 | 6 | (def transpose (partial apply map vector)) 7 | (def diagonal (partial map (comp first drop) (range))) 8 | (def indexed (partial map-indexed vector)) 9 | (def new-game #(vec (repeat % nil))) 10 | 11 | (h/defelem scoreboard 12 | [{:keys [history]} _] 13 | (let [sorted (cell= (indexed (->> history (sort-by second) reverse)))] 14 | (h/table :class "score" 15 | (h/tr (h/th "rank") (h/th "player") (h/th "score")) 16 | (h/loop-tpl :bindings [[rank [player score]] sorted] 17 | (h/tr 18 | (h/td :align "center" (h/text "~(inc rank)")) 19 | (h/td :align "center" (h/text "~{player}")) 20 | (h/td :align "center" (h/text "~{score}"))))))) 21 | 22 | (h/defelem game 23 | [{:keys [size history] :or {size 3 history (cell [])} :as attr} _] 24 | (let [rowsize (js/parseInt size) 25 | sizen (* rowsize rowsize) 26 | game (cell (new-game sizen)) 27 | rows (cell= (partition rowsize game)) 28 | columns (cell= (transpose rows)) 29 | diagonals (cell= [(diagonal rows) (diagonal (map reverse rows))]) 30 | runs (cell= (concat rows columns diagonals)) 31 | winner (cell= (->> runs 32 | (map set) 33 | (remove #(contains? % nil)) 34 | (filter #(= 1 (count %))) 35 | ffirst)) 36 | moves (cell= (->> (indexed game) 37 | (filter (comp nil? second)) 38 | (map first))) 39 | no-moves? (cell= (not (seq moves))) 40 | over (cell= (cond winner (str winner " won!") 41 | no-moves? "Cat's game.")) 42 | ai! (fn [] 43 | (when-not @over 44 | (swap! game assoc (rand-nth @moves) "O"))) 45 | play! (fn [i j] 46 | (let [idx (+ (* i rowsize) j)] 47 | (when (and (not @over) 48 | (nil? (get @game idx))) 49 | (swap! game assoc idx "X"))))] 50 | (reset! history {"X" 0 "O" 0 "cat" 0}) 51 | (cell= 52 | (when (or winner no-moves?) 53 | (swap! ~(cell history) update-in [(or winner "cat")] inc))) 54 | (h/div (dissoc attr :size :history) 55 | (h/table :class "tictac" 56 | (h/loop-tpl :bindings [[i row] (cell= (indexed rows))] 57 | (h/tr 58 | (h/loop-tpl :bindings [[j x] (cell= (indexed row))] 59 | (h/td :click #(and (play! @i @j) (ai!)) (h/text "~{x}")))))) 60 | (h/div :toggle over 61 | (h/p (h/text "~{over}")) 62 | (h/button :click #(reset! game (new-game sizen)) "Play Again"))))) 63 | -------------------------------------------------------------------------------- /demos-homepage/src/hoplon/twitter/bootstrap.cljs: -------------------------------------------------------------------------------- 1 | (ns hoplon.twitter.bootstrap 2 | (:require 3 | [hoplon.core :as h] 4 | [javelin.core :refer [cell cell=]])) 5 | 6 | (def ^:private cols-prefixes 7 | (for [i [:xs :sm :md :lg] j (range 1 13)] 8 | [(str "col-" (name i)) j])) 9 | 10 | (defn- mkreset [sep] 11 | (->> cols-prefixes 12 | (map (fn [[k v]] [(keyword (str k sep v)) false])) 13 | (into {} ))) 14 | 15 | (def ^:private cols-seps 16 | {:col "-" :push "-push-" :pull "-pull-" :offset "-offset-"}) 17 | 18 | (def ^:private cols-reset 19 | (->> cols-seps (map #(vector (key %) (mkreset (val %)))) (into {}))) 20 | 21 | (defn- mkcol-key [key m] 22 | (reduce-kv #(assoc %1 (keyword (str "col-" (name %2) (cols-seps key) %3)) true) {} m)) 23 | 24 | (defn- do-classes [elem key val] 25 | (h/do! elem :class (merge (cols-reset key) (mkcol-key key val)))) 26 | 27 | (defmethod h/do! :col [elem key val] (do-classes elem key val)) 28 | (defmethod h/do! :offset [elem key val] (do-classes elem key val)) 29 | (defmethod h/do! :push [elem key val] (do-classes elem key val)) 30 | (defmethod h/do! :pull [elem key val] (do-classes elem key val)) 31 | 32 | (defmethod h/do! :success [elem _ val] (h/do! elem :class {:has-success (boolean val)})) 33 | (defmethod h/do! :warning [elem _ val] (h/do! elem :class {:has-warning (boolean val)})) 34 | (defmethod h/do! :error [elem _ val] (h/do! elem :class {:has-error (boolean val)})) 35 | 36 | (h/defelem container [attr kids] (h/div :class "container" attr kids)) 37 | (h/defelem form-horizontal [attr kids] (h/form :role "form" :class "form-horizontal" attr kids)) 38 | (h/defelem control-label [attr kids] (h/label :class "control-label" attr kids)) 39 | (h/defelem form-group [attr kids] (h/div :class "form-group" attr kids)) 40 | (h/defelem checkbox [attr kids] (h/div :class "checkbox" attr kids)) 41 | 42 | (h/defelem active 43 | [{:keys [state]} [kid]] 44 | (kid :class (cell= {:active state}))) 45 | 46 | (h/defelem deck 47 | [{:keys [state]} kids] 48 | (->> kids 49 | (map-indexed #(active :state (cell= (= %1 state)) %2)))) 50 | 51 | (h/defelem selector 52 | [{:keys [state event]} kids] 53 | (->> kids 54 | (deck :state state) 55 | (map-indexed #(%2 event (fn [_] (reset! state %1)))))) 56 | 57 | (h/defelem tab-tab [attr [txt]] (h/a :href "javascript:void(0)" txt)) 58 | (h/defelem content [attr kids] (h/div kids)) 59 | (h/defelem tab [attr kids] [(tab-tab (:name attr)) (content kids)]) 60 | 61 | (def ^:private trans (partial apply map vector)) 62 | 63 | (h/defelem tabs 64 | [{:keys [state]} kids] 65 | (let [[trigs ctnrs] (trans (partition 2 kids)) 66 | state (or state (cell 0)) 67 | trigs (selector :state state :event :click (map h/li trigs)) 68 | ctnrs (deck :state state (map #(% :class "tab-pane") ctnrs))] 69 | [(h/ul :class "nav nav-tabs" trigs) 70 | (h/div :class "tab-content" ctnrs)])) 71 | -------------------------------------------------------------------------------- /validated-form/public/css/main.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Globals 3 | */ 4 | 5 | /* Links */ 6 | a, 7 | a:focus, 8 | a:hover { 9 | color: #fff; 10 | } 11 | 12 | /* 13 | * Base structure 14 | */ 15 | 16 | html, 17 | body { 18 | height: 100%; 19 | } 20 | body { 21 | box-shadow: inset 0 0 100px rgba(0,0,0,.5); 22 | } 23 | 24 | /* Extra markup and styles for table-esque vertical and horizontal centering */ 25 | .site-wrapper { 26 | display: table; 27 | width: 100%; 28 | height: 100%; /* For at least Firefox */ 29 | min-height: 100%; 30 | } 31 | .site-wrapper-inner { 32 | display: table-cell; 33 | vertical-align: top; 34 | } 35 | .cover-container { 36 | margin-right: auto; 37 | margin-left: auto; 38 | } 39 | 40 | .cover-heading { 41 | text-align: center; 42 | } 43 | 44 | /* Padding for spacing */ 45 | .inner { 46 | padding: 30px; 47 | } 48 | 49 | 50 | /* 51 | * Header 52 | */ 53 | .masthead-brand { 54 | margin-top: 10px; 55 | margin-bottom: 10px; 56 | } 57 | 58 | .masthead-nav > li { 59 | display: inline-block; 60 | } 61 | .masthead-nav > li + li { 62 | margin-left: 20px; 63 | } 64 | .masthead-nav > li > a { 65 | padding-right: 0; 66 | padding-left: 0; 67 | font-size: 16px; 68 | font-weight: bold; 69 | color: #fff; /* IE8 proofing */ 70 | color: rgba(255,255,255,.75); 71 | border-bottom: 2px solid transparent; 72 | } 73 | .masthead-nav > li > a:hover, 74 | .masthead-nav > li > a:focus { 75 | background-color: transparent; 76 | border-bottom-color: rgba(255,255,255,.25); 77 | } 78 | .masthead-nav > .active > a, 79 | .masthead-nav > .active > a:hover, 80 | .masthead-nav > .active > a:focus { 81 | color: #fff; 82 | border-bottom-color: #fff; 83 | } 84 | 85 | @media (min-width: 768px) { 86 | .masthead-brand { 87 | float: left; 88 | } 89 | .masthead-nav { 90 | float: right; 91 | } 92 | } 93 | 94 | 95 | /* 96 | * Cover 97 | */ 98 | 99 | .cover { 100 | padding: 0 20px; 101 | } 102 | .cover .btn-lg { 103 | padding: 10px 20px; 104 | font-weight: bold; 105 | } 106 | 107 | 108 | /* 109 | * Footer 110 | */ 111 | 112 | .mastfoot { 113 | color: #999; /* IE8 proofing */ 114 | color: rgba(255,255,255,.5); 115 | } 116 | 117 | .masthead, 118 | .mastfoot, 119 | .cover-container { 120 | width: 400px; /* Must be percentage or pixels for horizontal alignment */ 121 | } 122 | 123 | 124 | /* 125 | * Affix and center 126 | */ 127 | 128 | @media (min-width: 768px) { 129 | /* Pull out the header and footer */ 130 | .masthead { 131 | position: fixed; 132 | top: 0; 133 | } 134 | .mastfoot { 135 | position: fixed; 136 | bottom: 0; 137 | } 138 | /* Start the vertical centering */ 139 | .site-wrapper-inner { 140 | vertical-align: middle; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /contacts/src/demo/contacts.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.contacts 2 | (:require 3 | [clojure.string :as str] 4 | [hoplon.core :as h] 5 | [hoplon.goog] 6 | [javelin.core :refer [cell cell= dosync]])) 7 | 8 | (def my-contacts 9 | (cell #{{:first "Ben" :last "Bitdiddle" :email "benb@mit.edu"} 10 | {:first "Alyssa" :middle-initial "P" :last "Hacker" :email "aphacker@mit.edu"} 11 | {:first "Eva" :middle "Lu" :last "Ator" :email "eval@mit.edu"} 12 | {:first "Louis" :last "Reasoner" :email "prolog@mit.edu"} 13 | {:first "Cy" :middle-initial "D" :last "Effect" :email "bugs@mit.edu"} 14 | {:first "Lem" :middle-initial "E" :last "Tweakit" :email "morebugs@mit.edu"}})) 15 | 16 | (defn middle-name [{:keys [middle middle-initial]}] 17 | (cond 18 | middle (str " " middle) 19 | middle-initial (str " " middle-initial "."))) 20 | 21 | (defn display-name [{:keys [first last] :as contact}] 22 | (str last ", " first (middle-name contact))) 23 | 24 | (h/defelem contact-list [{:keys [from sorted-by] :or {sorted-by identity}}] 25 | (h/loop-tpl :bindings [contact (cell= (sort-by sorted-by from))] 26 | (h/li (h/span (cell= (display-name contact))) 27 | (h/button :click #(swap! from disj @contact) "Delete")))) 28 | 29 | (defn parse-contact [contact-str] 30 | (let [[first middle last :as parts] (str/split contact-str #"\s+") 31 | [first last middle] (if (nil? last) [first middle] [first last middle]) 32 | middle (when middle (str/replace middle "." "")) 33 | c (if middle (count middle) 0)] 34 | (when (>= (count parts) 2) 35 | (cond-> {:first first :last last} 36 | (== c 1) (assoc :middle-initial middle) 37 | (>= c 2) (assoc :middle middle))))) 38 | 39 | (h/defelem contact-input [{:keys [to]} [label]] 40 | (let [new-contact (cell "") 41 | parsed (cell= (parse-contact new-contact))] 42 | (h/div 43 | (h/input 44 | :value new-contact 45 | :input #(reset! new-contact @%)) 46 | (h/button 47 | :click #(when-let [c @parsed] 48 | (dosync (swap! to conj c) 49 | (reset! new-contact ""))) 50 | :disabled (cell= (not parsed)) 51 | label) 52 | (h/pre (cell= (pr-str parsed)))))) 53 | 54 | (h/defelem contact [] 55 | (h/div 56 | (h/h2 "Contact list") 57 | (h/ul (contact-list :from my-contacts :sorted-by :last)) 58 | (contact-input :to my-contacts "Add contact") 59 | (h/hr) 60 | (h/p (h/em "Note: The Add contact button is disabled until you enter a valid contact. A valid contact consists of two or three whitespace-delimited names.")) 61 | (h/a :href "https://github.com/tailrecursion/hoplon-demos/tree/master/contacts" "Source code on Github"))) 62 | 63 | (defn mount-components [] 64 | (.replaceChildren (.getElementById js/document "app") 65 | (contact))) 66 | 67 | (defn start [] 68 | (mount-components) 69 | (js/console.log "Starting...")) 70 | 71 | (defn stop [] 72 | (js/console.log "Stopping...")) 73 | 74 | (defn init [] 75 | (js/console.log "Initializing...") 76 | (start)) 77 | -------------------------------------------------------------------------------- /demos-homepage/public/image/Lambda_lc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 20 | 36 | 38 | 39 | 41 | image/svg+xml 42 | 44 | 45 | 46 | 47 | 51 | 54 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /plotSVG/src/demo/plot_svg.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.plot-svg 2 | (:require 3 | [clojure.string :as str] 4 | [hoplon.core :as h] 5 | [hoplon.jquery] 6 | [demo.chart :as c] 7 | [javelin.core :refer [cell cell= defc defc= dosync]])) 8 | 9 | (defn by-id [id] 10 | (.getElementById js/document (name id))) 11 | 12 | (defn val-id [id] 13 | (h/do! (by-id id) :value)) 14 | 15 | ;; Source code 16 | (def src-url "https://github.com/hoplon/demos/blob/master/plotSVG") 17 | 18 | ;; colors 19 | (def c1 "#006666") 20 | (def c2 "#660066") 21 | 22 | ;; returns a seq of random [x y] pairs, 0 <= y <= 9 23 | (defn data! [] 24 | (vec (for [x (range 0 11)] [x (rand-int 11)]))) 25 | 26 | ;; push a random value onto the end of a data series 27 | (defn add! [data] 28 | (conj data [(-> data last first inc) (rand-int 11)])) 29 | 30 | ;; two data series 31 | (defc data1 (data!)) 32 | (defc data2 (data!)) 33 | 34 | ;; user configurations "knobs" 35 | (defc pwidth 400) 36 | (defc paused? false) 37 | 38 | ;; "clipped" data (moving strip-chart) 39 | (defc= series1 (take-last 10 data1)) 40 | (defc= series2 (take-last 10 data2)) 41 | 42 | ;; configure the plotting envelope (linear scale) 43 | (defc= chart1 44 | (let [min-x (max (ffirst series1) (ffirst series2)) 45 | max-x (min (first (last series1)) (first (last series2)))] 46 | (c/config 47 | :width pwidth :height 200 48 | :min-x min-x :max-x max-x 49 | :min-y 0 :max-y 10))) 50 | 51 | (h/defelem plot-svg [] 52 | (h/div :css {:width "400px" 53 | :margin "0 auto" 54 | :text-align "center" 55 | :padding "20px" 56 | :font-family "sans-serif"} 57 | 58 | (h/h2 "Hoplon • Chart Demo") 59 | 60 | (h/hr) 61 | 62 | (h/p 63 | "Click the plot area to pause/play. " 64 | "Hover over a point to see its coordinates. " 65 | "Slide the slider to adjust plot width.") 66 | 67 | (c/container 68 | :chart chart1 69 | :css (cell= {:border "1px solid black" 70 | :margin-left (str "-" (- (/ (:width chart1) 2) 200) "px")}) 71 | :click #(swap! paused? not) 72 | 73 | ;; draw shading first so it doesn't cover lines or points 74 | (c/polygon :chart chart1 :data series1 :css {:fill c1 :stroke "none" :fill-opacity 0.5}) 75 | (c/polygon :chart chart1 :data series2 :css {:fill c2 :stroke "none" :fill-opacity 0.5}) 76 | 77 | ;; draw lines 78 | (c/polyline :chart chart1 :data series1 :css {:fill "none" :stroke c1 :stroke-width 2}) 79 | (c/polyline :chart chart1 :data series2 :css {:fill "none" :stroke c2 :stroke-width 2}) 80 | 81 | ;; draw points last so they're not covered by lines or shading 82 | (c/points-circle :chart chart1 :data series1 :r 3 :css {:stroke c1 :fill c1}) 83 | (c/points-rect :chart chart1 :data series2 :width 6 :height 6 :css {:stroke c2 :fill c2})) 84 | 85 | (h/br) 86 | 87 | ;; slider that lets the user change the width of the plot 88 | (h/input 89 | :id "w" :style "width:400px" 90 | :type "range" :min 400 :max 800 :step 1 :value 400 91 | :change #(reset! pwidth (val-id "w"))) 92 | 93 | (h/p (h/a :href src-url "Source Code")))) 94 | 95 | (defn mount-components [] 96 | (.replaceChildren (.getElementById js/document "app") 97 | (plot-svg))) 98 | 99 | (defn start [] 100 | (mount-components) 101 | (js/console.log "Starting...")) 102 | 103 | (defn stop [] 104 | (js/console.log "Stopping...")) 105 | 106 | (defn init [] 107 | (js/console.log "Initializing...") 108 | (start) 109 | ;; add data random data points every 1000ms 110 | (h/with-init! 111 | (h/with-interval 1000 112 | (when-not @paused? 113 | (swap! data1 add!) 114 | (swap! data2 add!))))) 115 | -------------------------------------------------------------------------------- /inputs/src/index.cljs.hl: -------------------------------------------------------------------------------- 1 | (page "index.html") 2 | 3 | ;; Cells for each input example. 4 | (defc text-input "") 5 | (defc range-input 20) 6 | (defc select-input "green") 7 | (defc multi-select #{"green"}) 8 | (defc check-box false) 9 | (defc radio-input "b") 10 | 11 | (html 12 | (head 13 | (title "Hoplon • Inputs")) 14 | (body 15 | (h1 16 | "Inputs and state in Hoplon") 17 | (p 18 | "Each input example has a corresponding Javelin cell. 19 | When you change the input its cell is updated.") 20 | (h2 "A simple text input") 21 | (p "This shows that you can have more than one field pointing to the same 22 | cell. Edit one input and see everything change in sync.") 23 | (input 24 | :type "text" 25 | :placeholder "Type something here" 26 | 27 | ;; The cell is used as the value of the input. 28 | :value text-input 29 | 30 | ;; The on-keyup event fires on every keystroke for demo purposes. In 31 | ;; practice you should only update on-blur or on-change. 32 | :keyup #(reset! text-input @%)) 33 | ;; On all the examples we are using the event to get the value of the 34 | ;; input. So no id is necessary. 35 | 36 | ;; This second input is just to show that you can point two inputs to the 37 | ;; same cell and update them in sync. It's the same as the first input. 38 | (input 39 | :type "text" 40 | :placeholder "Type something here" 41 | :value text-input 42 | :keyup #(reset! text-input @%)) 43 | (p (text "Value of text input: ~{text-input}")) 44 | 45 | (h2 "A range input") 46 | (p "Every example gets the value from a cell and updates it when it changes.") 47 | (input 48 | :style "width:400px" 49 | :type "range" :min 0 :max 100 :step 1 50 | :value range-input 51 | :input #(reset! range-input @%)) 52 | (p (text "Value of range input: ~{range-input}")) 53 | 54 | (h2 "A select input") 55 | (select 56 | :change #(reset! select-input @%) 57 | :value select-input 58 | (option :selected (cell= (= select-input "blue")) :value "blue" "blue") 59 | (option :selected (cell= (= select-input "green")) :value "green" "green") 60 | (option :selected (cell= (= select-input "gold")) :value "gold" "gold") 61 | (option :selected (cell= (= select-input "indigo")) :value "indigo" "indigo")) 62 | (p (text "Value of select input: ~{select-input}")) 63 | 64 | (h2 "Multiple select input") 65 | (select 66 | 67 | ;; The handler is more complex here because of the parsing of the 68 | ;; selectedOptions object. The cell is a set in this case. 69 | :change #(let [options (.. % -target -selectedOptions) 70 | l (.-length options)] 71 | (reset! multi-select 72 | (set 73 | (for [i (range l)] 74 | (.-value (.item options i)))))) 75 | :multiple "true" 76 | 77 | ;; You need to check each option value against the multi-select cell to 78 | ;; se if it should be selected. 79 | (option :selected (cell= (multi-select "blue")) :value "blue" "blue") 80 | (option :selected (cell= (multi-select "green")) :value "green" "green") 81 | (option :selected (cell= (multi-select "gold")) :value "gold" "gold") 82 | (option :selected (cell= (multi-select "indigo")) :value "indigo" "indigo")) 83 | (p (text "Value of multiple select input: ~{multi-select}")) 84 | 85 | (h2 "A checkbox") 86 | (label 87 | (input 88 | :type "checkbox" 89 | 90 | ;; On checkboxes you need to return true from the handler or the 91 | ;; checkbox will not uncheck (at the moment). 92 | :click #(do 93 | (swap! check-box not) 94 | true) 95 | :value check-box) 96 | "Checkbox") 97 | (p (text "Value of checkbox: ~{check-box}")) 98 | 99 | (h2 "A radio button") 100 | 101 | ;; Radio buttons require more code, but work fine with cells too. 102 | (label 103 | (input 104 | :type "radio" 105 | :click #(reset! radio-input @%) 106 | 107 | ;; You need to check each input to se if it is checked. 108 | :checked (cell= (= "a" radio-input)) 109 | :name "radio-group" 110 | :value "a") 111 | "a") 112 | 113 | (label 114 | (input 115 | :type "radio" 116 | :click #(reset! radio-input @%) 117 | :checked (cell= (= "b" radio-input)) 118 | :name "radio-group" 119 | :value "b") 120 | "b") 121 | (p (text "Selected radio: ~{radio-input}")))) 122 | -------------------------------------------------------------------------------- /inputs/src/demo/inputs.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.inputs 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.goog] 5 | [javelin.core :refer [cell cell= defc]])) 6 | 7 | ;; Cells for each input example. 8 | (defc text-input "") 9 | (defc range-input 20) 10 | (defc select-input "green") 11 | (defc multi-select #{"green"}) 12 | (defc check-box true) 13 | (defc radio-input "b") 14 | 15 | (h/defelem inputs [] 16 | 17 | (h/div 18 | (h/h1 19 | "Inputs and state in Hoplon") 20 | (h/p 21 | "Each input example has a corresponding Javelin cell. 22 | When you change the input its cell is updated.") 23 | (h/h2 "A simple text input") 24 | (h/p "This shows that you can have more than one field pointing to the same 25 | cell. Edit one input and see everything change in sync.") 26 | (h/input 27 | :type "text" 28 | :placeholder "Type something here" 29 | 30 | ;; The cell is used as the value of the input. 31 | :value text-input 32 | 33 | ;; The on-keyup event fires on every keystroke for demo purposes. In 34 | ;; practice you should only update on-blur or on-change. 35 | :keyup #(reset! text-input @%)) 36 | ;; On all the examples we are using the event to get the value of the 37 | ;; input. So no id is necessary. 38 | 39 | ;; This second input is just to show that you can point two inputs to the 40 | ;; same cell and update them in sync. It's the same as the first input. 41 | (h/input 42 | :type "text" 43 | :placeholder "Type something here" 44 | :value text-input 45 | :keyup #(reset! text-input @%)) 46 | (h/p (h/text "Value of text input: ~{text-input}")) 47 | 48 | (h/h2 "A range input") 49 | (h/p "Every example gets the value from a cell and updates it when it changes.") 50 | (h/input 51 | :style "width:400px" 52 | :type "range" :min 0 :max 100 :step 1 53 | :value range-input 54 | :input #(reset! range-input @%)) 55 | (h/p (h/text "Value of range input: ~{range-input}")) 56 | 57 | (h/h2 "A select input") 58 | (h/select 59 | :change #(reset! select-input @%) 60 | :value select-input 61 | (h/option :selected (cell= (= select-input "blue")) :value "blue" "blue") 62 | (h/option :selected (cell= (= select-input "green")) :value "green" "green") 63 | (h/option :selected (cell= (= select-input "gold")) :value "gold" "gold") 64 | (h/option :selected (cell= (= select-input "indigo")) :value "indigo" "indigo")) 65 | (h/p (h/text "Value of select input: ~{select-input}")) 66 | 67 | (h/h2 "Multiple select input") 68 | (h/select 69 | 70 | ;; The handler is more complex here because of the parsing of the 71 | ;; selectedOptions object. The cell is a set in this case. 72 | :change #(let [options (.. % -target -selectedOptions) 73 | l (.-length options)] 74 | (reset! multi-select 75 | (set 76 | (for [i (range l)] 77 | (.-value (.item options i)))))) 78 | :multiple "true" 79 | 80 | ;; You need to check each option value against the multi-select cell to 81 | ;; se if it should be selected. 82 | (h/option :selected (cell= (multi-select "blue")) :value "blue" "blue") 83 | (h/option :selected (cell= (multi-select "green")) :value "green" "green") 84 | (h/option :selected (cell= (multi-select "gold")) :value "gold" "gold") 85 | (h/option :selected (cell= (multi-select "indigo")) :value "indigo" "indigo")) 86 | (h/p (h/text "Value of multiple select input: ~{multi-select}")) 87 | 88 | (h/h2 "A checkbox") 89 | (h/label 90 | (h/input 91 | :type "checkbox" 92 | 93 | ;; On checkboxes you need to return true from the handler or the 94 | ;; checkbox will not uncheck (at the moment). 95 | :click #(do 96 | (swap! check-box not) 97 | true) 98 | :value check-box) 99 | "Checkbox") 100 | (h/p (h/text "Value of checkbox: ~{check-box}")) 101 | 102 | (h/h2 "A radio button") 103 | 104 | ;; Radio buttons require more code, but work fine with cells too. 105 | (h/label 106 | (h/input 107 | :type "radio" 108 | :click #(reset! radio-input (.getAttribute (.-target %) "data-id")) 109 | 110 | ;; You need to check each input to se if it is checked. 111 | :checked (cell= (= "a" radio-input)) 112 | :data-id "a" 113 | :name "radio-group") 114 | "a") 115 | 116 | (h/label 117 | (h/input 118 | :type "radio" 119 | :click #(reset! radio-input (.getAttribute (.-target %) "data-id")) 120 | :checked (cell= (= "b" radio-input)) 121 | :data-id "b" 122 | :name "radio-group") 123 | "b") 124 | (h/p (h/text "Selected radio: ~{radio-input}")))) 125 | 126 | (defn mount-components [] 127 | (.replaceChildren (.getElementById js/document "app") 128 | (inputs))) 129 | 130 | (defn start [] 131 | (mount-components) 132 | (js/console.log "Starting...")) 133 | 134 | (defn stop [] 135 | (js/console.log "Stopping...")) 136 | 137 | (defn init [] 138 | (js/console.log "Initializing...") 139 | (start)) 140 | -------------------------------------------------------------------------------- /todoFRP/src/demo/todo_frp.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.todo-frp 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.storage-atom :refer [local-storage]] 5 | [hoplon.jquery] 6 | [javelin.core :refer [cell cell= defc defc= dosync with-let]])) 7 | 8 | ;; utility functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 9 | 10 | (defn pluralize 11 | [word count] 12 | (str word (when (not= 1 count) "s"))) 13 | 14 | (def mapv-indexed (comp vec map-indexed)) 15 | 16 | (defn dissocv [v i] 17 | (let [z (- (dec (count v)) i)] 18 | (cond (neg? z) v 19 | (zero? z) (pop v) 20 | (pos? z) (into (subvec v 0 i) (subvec v (inc i)))))) 21 | 22 | (defn decorate [todo route editing i] 23 | (let [{done? :completed text :text} todo] 24 | (-> todo 25 | (assoc :editing (= editing i) 26 | :visible (and (not (empty? text)) 27 | (or (= "#/" route) 28 | (and (= "#/active" route) (not done?)) 29 | (and (= "#/completed" route) done?))))))) 30 | 31 | (defn route-cell 32 | "Defines a cell whose value is the URI fragment." 33 | [& [default]] 34 | (let [c (cell (.. js/window -location -hash))] 35 | (with-let [_ (cell= (or (and (seq c) c) default))] 36 | (-> js/window 37 | (.addEventListener "hashchange" #(reset! c (.. js/window -location -hash))))))) 38 | 39 | ;; persisted state cell (AKA: stem cell) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 40 | 41 | (def state (-> (cell []) (local-storage ::store))) 42 | 43 | ;; local state cells ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 44 | 45 | (defc loaded? false) 46 | (defc editing nil) 47 | (def route (route-cell "#/")) 48 | 49 | ;; formula cells (computed state) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 50 | 51 | (defc= completed (filter :completed state)) 52 | (defc= active (remove :completed state)) 53 | (defc= plural-item (pluralize "item" (count active))) 54 | (defc= todos (mapv-indexed #(list %1 (decorate %2 route editing %1)) state)) 55 | (defc= reverse-todos (reverse todos)) 56 | 57 | ;; state transition functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 58 | 59 | (defn todo [t] {:completed false :text t}) 60 | (defn destroy! [i] (swap! state dissocv i)) 61 | (defn done! [i v] (swap! state update-in [i :completed] not)) 62 | (defn clear-done! [& _] (swap! state #(vec (remove :completed %)))) 63 | (defn new! [t] (when (seq t) (swap! state conj (todo t)))) 64 | (defn all-done! [v] (swap! state #(mapv (fn [x] (assoc x :completed v)) %))) 65 | (defn editing! [i v] (reset! editing (if v i nil))) 66 | (defn text! [i v] (if (empty? v) (destroy! i) (swap! state assoc-in [i :text] v))) 67 | 68 | (h/defelem todo-mvc [] 69 | (h/div 70 | (h/section :id "todoapp" 71 | (h/header :id "header" 72 | (h/h1 "todos") 73 | (let [new-todo-txt (cell "")] 74 | (h/form 75 | :submit #(do (new! @new-todo-txt) 76 | (reset! new-todo-txt "") 77 | (.preventDefault %)) 78 | (h/input 79 | :id "new-todo" 80 | :type "text" 81 | :autofocus true 82 | :placeholder "What needs to be done?" 83 | :value new-todo-txt 84 | :change #(reset! new-todo-txt @%) 85 | :blur #(reset! new-todo-txt ""))))) 86 | (h/section 87 | :id "main" 88 | :toggle (cell= (not (and (empty? active) (empty? completed)))) 89 | (h/input 90 | :id "toggle-all" 91 | :type "checkbox" 92 | :attr (cell= {:checked (seq active)}) 93 | :click #(all-done! true)) 94 | (h/label :for "toggle-all" 95 | "Mark all as complete") 96 | (h/ul :id "todo-list" 97 | (h/loop-tpl 98 | :bindings [[i {edit? :editing done? :completed todo-text :text show? :visible}] reverse-todos] 99 | (h/li 100 | :class (cell= {:completed done? :editing edit?}) 101 | :toggle show? 102 | (h/div :class "view" 103 | :dblclick #(editing! @i true) 104 | (h/input 105 | :type "checkbox" 106 | :class "toggle" 107 | :attr (cell= {:checked done?}) 108 | :click #(done! @i @%)) 109 | (h/label (h/text "~{todo-text}")) 110 | (h/button 111 | :type "submit" 112 | :class "destroy" 113 | :click #(destroy! @i))) 114 | (h/form 115 | :submit #(do (editing! @i false) 116 | (.preventDefault %)) 117 | (h/input 118 | :type "text" 119 | :class "edit" 120 | :value todo-text 121 | :focus edit? 122 | :blur #(when @edit? (editing! @i false)) 123 | :change #(when @edit? (text! @i @%)))))))) 124 | (h/footer 125 | :id "footer" 126 | :toggle (cell= (not (and (empty? active) (empty? completed)))) 127 | (h/span :id "todo-count" 128 | (h/strong (h/text "~(count active) ")) 129 | (h/span (h/text "~{plural-item} left"))) 130 | (h/ul :id "filters" 131 | (h/li (h/a :href "#/" :class (cell= {:selected (= "#/" route)}) "All")) 132 | (h/li (h/a :href "#/active" :class (cell= {:selected (= "#/active" route)}) "Active")) 133 | (h/li (h/a :href "#/completed" :class (cell= {:selected (= "#/completed" route)}) "Completed"))) 134 | (h/button 135 | :type "submit" 136 | :id "clear-completed" 137 | :click #(clear-done!) 138 | (h/text "Clear completed (~(count completed))")))) 139 | (h/footer :id "info" 140 | (h/p "Double-click to edit a todo") 141 | (h/p "Part of " (h/a :href "http://github.com/hoplon/demos/" "Hoplon demos")))) 142 | 143 | ) 144 | 145 | (defn mount-components [] 146 | (.replaceChildren (.getElementById js/document "app") 147 | (todo-mvc))) 148 | 149 | (defn start [] 150 | (mount-components) 151 | (js/console.log "Starting...")) 152 | 153 | (defn stop [] 154 | (js/console.log "Stopping...")) 155 | 156 | (defn init [] 157 | (js/console.log "Initializing...") 158 | (start)) 159 | -------------------------------------------------------------------------------- /infinite-scroll-paginated/src/demo/infinite_scroll_paginated.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.infinite-scroll-paginated 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.jquery] 5 | [javelin.core :refer [cell cell= dosync defc defc=]] 6 | [clojure.string :as str])) 7 | 8 | (defn generate-numbers 9 | "This generates a data vector with ten maps of sequential numbers. 10 | Example: [{:number \"0\"} {:number \"1\"} ... {:number \"9\"}]" 11 | [n] 12 | (vec (map (fn [x] {:number (str x)}) (range (* (dec n) 10) 13 | (+ (* (dec n) 10) 10))))) 14 | (defn generate-data 15 | "This generates a page map with this format 16 | {\"1\" {:page 1, :data [...]}}" 17 | [n] 18 | {(str n) {:page n 19 | :data (generate-numbers n)}}) 20 | 21 | (def fake-api 22 | "This is a vector of pages from 1 to 99" 23 | (into {} (map generate-data (range 1 100)))) 24 | 25 | ;; The application starts here. 26 | 27 | (defc state "The current state, a vector of loaded pages." []) 28 | (defc error "It will be a error message when a page is not found." nil) 29 | (defc loading "When it isn't empty, something is loading." []) 30 | 31 | (defc= loading? "True when there is something loading" (seq loading)) 32 | 33 | (defc= sorted-state 34 | "A vector of pages sorted by page" 35 | (vec (sort-by :page state))) 36 | 37 | (defc pages-loaded "Page numbers of loaded pages." []) 38 | 39 | (defc= prev-page 40 | "Previous page that should be loaded (when scrolling up)." 41 | (dec (apply min pages-loaded))) 42 | 43 | (defc= next-page 44 | "Next page number that should be loaded." 45 | (inc (apply max pages-loaded))) 46 | 47 | (defc last-scroll "Last scroll position" 0) 48 | (defc hash-scroll "When false disable the handler to scroll on hashchange." true) 49 | (defc timeout-id "Last setTimeout id so we can cancel it if needed." 0) 50 | 51 | (defn get-page 52 | "Get a page from the fake api and run a callback on the result." 53 | [p callback] 54 | (callback (fake-api p))) 55 | 56 | (defn fetch-page! 57 | "Put the page p on the state and on the pages-loaded." 58 | [p] 59 | (when-not @loading? 60 | (reset! error nil) 61 | (swap! loading conj :loading) 62 | (let [q (str p)] 63 | (get-page q 64 | #(do 65 | (swap! loading pop) 66 | (when % 67 | (swap! state conj %) 68 | (swap! pages-loaded conj p)) 69 | (when-not % (reset! error "Sem mais páginas"))))))) 70 | 71 | (defn fetch-next-page! 72 | "Put the next page on the state and pages-loaded." 73 | [] 74 | (fetch-page! @next-page)) 75 | 76 | (defn fetch-prev-page! 77 | "Put the previous page on the state and pages-loaded and fix the scroll 78 | position." 79 | [] 80 | (let [p @prev-page] 81 | (fetch-page! p) 82 | (.scrollTop (js/jQuery js/window) 83 | (+ (.scrollTop (js/jQuery js/window)) 84 | (.height (js/jQuery (str "#page-" p))))))) 85 | 86 | (defn mostly-visible 87 | "Returns true if the element is the most visible on screen." 88 | [el] 89 | (let [w (js/jQuery js/window) 90 | vertical-scroll (.scrollTop w) 91 | window-height (.height w) 92 | el-top (.-top (.offset (js/jQuery el))) 93 | el-height (.height (js/jQuery el)) 94 | el-bottom (+ el-top el-height)] 95 | (and (> (- el-bottom (* el-height 0.25)) vertical-scroll) 96 | (< el-top (+ vertical-scroll (* 0.5 window-height)))))) 97 | 98 | 99 | 100 | (h/defelem infinite-scroll-paginated [] 101 | (h/div 102 | (h/div :id "header" 103 | (h/h1 "Infinite Scrolling") 104 | (h/p "Scroll down to see more pages...")) 105 | (h/div :id "wrapper" 106 | (h/div :id "loading" :toggle loading? "LOADING IMAGES...") 107 | (h/div :id "error" :toggle error 108 | "No more pages: " 109 | (h/a :href "javascript:void(0)" :click fetch-next-page! "try again")) 110 | (h/div 111 | (h/loop-tpl :bindings [{p :page d :data} sorted-state] 112 | (h/div 113 | :id (cell= (str "page-" p)) 114 | :class "item-page" 115 | :data-url (cell= (str "#/page/" p)) 116 | (h/h1 :id (cell= (str "page-" p)) 117 | (h/text "Page: ~{p}")) 118 | (h/loop-tpl :bindings [{it :number} d] 119 | (h/div 120 | :css {:height 100} 121 | (h/h2 (h/text "Number: ~{it}")))))))))) 122 | 123 | (defn mount-components [] 124 | (.replaceChildren (.getElementById js/document "app") 125 | (infinite-scroll-paginated))) 126 | 127 | (defn start [] 128 | (mount-components) 129 | (if (= (.-length (.-hash (.-location js/window))) 0) 130 | (fetch-page! 1) 131 | (fetch-page! (js/parseInt (last (str/split (.-hash (.-location js/window)) #"/"))))) 132 | 133 | (h/on! js/window :scroll 134 | #(let [w (js/jQuery js/window) 135 | vertical-scroll (.scrollTop w) 136 | window-height (.height w) 137 | document-height (.height (js/jQuery js/document)) 138 | at-end? (>= vertical-scroll 139 | (* 0.9 (- document-height window-height))) 140 | header-height (.height (js/jQuery "#header")) 141 | at-begin? (= vertical-scroll 0)] 142 | ;; This part of code checks what is the page most visible on screen and 143 | ;; changes the hash if that changed. It disables the scroll on 144 | ;; hashchange event and reenables it after. 145 | (when (> (.abs js/Math (- vertical-scroll @last-scroll)) 146 | (* 0.1 window-height)) 147 | (reset! last-scroll vertical-scroll) 148 | (.each (js/jQuery ".item-page") 149 | (fn [i v] 150 | (if (mostly-visible v) 151 | (do 152 | (js/clearTimeout @timeout-id) 153 | (reset! hash-scroll false) 154 | (set! (.-hash (.-location js/window)) 155 | (.attr (js/jQuery v) "data-url")) 156 | (reset! timeout-id (js/setTimeout 157 | (fn [] (reset! hash-scroll true)) 158 | 500))))))) 159 | ;; This checks if we are scrolling near the end of the page or at the 160 | ;; beginning. Them it loads the next or previous page. 161 | (if at-end? 162 | (fetch-next-page!) 163 | (when at-begin? 164 | (fetch-prev-page!))))) 165 | 166 | ;; This part makes the page scroll on hashchange if the page asked is already 167 | ;; loaded and triggers a reload when it isn't. 168 | (h/on! js/window :hashchange 169 | #(when @hash-scroll 170 | (let [p (last (str/split (.-hash (.-location js/window)) #"/")) 171 | el (js/jQuery (str "#page-"p))] 172 | (if (> (.-length el) 0) 173 | (let [position (.position el) 174 | tp (.-top position) 175 | b (js/jQuery "html,body")] 176 | (.animate b (clj->js {:scrollTop tp}))) 177 | (do 178 | (reset! last-scroll 0) 179 | (.reload (.-location js/window) true)))))) 180 | (js/console.log "Starting...")) 181 | 182 | (defn stop [] 183 | (js/console.log "Stopping...")) 184 | 185 | (defn init [] 186 | (js/console.log "Initializing...") 187 | (start)) 188 | -------------------------------------------------------------------------------- /todoFRP/public/css/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | color: inherit; 16 | -webkit-appearance: none; 17 | /*-moz-appearance: none;*/ 18 | -ms-appearance: none; 19 | -o-appearance: none; 20 | appearance: none; 21 | } 22 | 23 | body { 24 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 25 | line-height: 1.4em; 26 | background: #eaeaea url('../image/bg.png'); 27 | color: #4d4d4d; 28 | width: 550px; 29 | margin: 0 auto; 30 | -webkit-font-smoothing: antialiased; 31 | -moz-font-smoothing: antialiased; 32 | -ms-font-smoothing: antialiased; 33 | -o-font-smoothing: antialiased; 34 | font-smoothing: antialiased; 35 | } 36 | 37 | #todoapp { 38 | background: #fff; 39 | background: rgba(255, 255, 255, 0.9); 40 | margin: 130px 0 40px 0; 41 | border: 1px solid #ccc; 42 | position: relative; 43 | border-top-left-radius: 2px; 44 | border-top-right-radius: 2px; 45 | box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2), 46 | 0 25px 50px 0 rgba(0, 0, 0, 0.15); 47 | } 48 | 49 | #todoapp:before { 50 | content: ''; 51 | border-left: 1px solid #f5d6d6; 52 | border-right: 1px solid #f5d6d6; 53 | width: 2px; 54 | position: absolute; 55 | top: 0; 56 | left: 40px; 57 | height: 100%; 58 | } 59 | 60 | #todoapp input::-webkit-input-placeholder { 61 | font-style: italic; 62 | } 63 | 64 | #todoapp input:-moz-placeholder { 65 | font-style: italic; 66 | color: #a9a9a9; 67 | } 68 | 69 | #todoapp h1 { 70 | position: absolute; 71 | top: -120px; 72 | width: 100%; 73 | font-size: 70px; 74 | font-weight: bold; 75 | text-align: center; 76 | color: #b3b3b3; 77 | color: rgba(255, 255, 255, 0.3); 78 | text-shadow: -1px -1px rgba(0, 0, 0, 0.2); 79 | -webkit-text-rendering: optimizeLegibility; 80 | -moz-text-rendering: optimizeLegibility; 81 | -ms-text-rendering: optimizeLegibility; 82 | -o-text-rendering: optimizeLegibility; 83 | text-rendering: optimizeLegibility; 84 | } 85 | 86 | #header { 87 | padding-top: 15px; 88 | border-radius: inherit; 89 | } 90 | 91 | #header:before { 92 | content: ''; 93 | position: absolute; 94 | top: 0; 95 | right: 0; 96 | left: 0; 97 | height: 15px; 98 | z-index: 2; 99 | border-bottom: 1px solid #6c615c; 100 | background: #8d7d77; 101 | background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8))); 102 | background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 103 | background: -moz-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 104 | background: -o-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 105 | background: -ms-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 106 | background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8)); 107 | filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670'); 108 | border-top-left-radius: 1px; 109 | border-top-right-radius: 1px; 110 | } 111 | 112 | #new-todo, 113 | .edit { 114 | position: relative; 115 | margin: 0; 116 | width: 100%; 117 | font-size: 24px; 118 | font-family: inherit; 119 | line-height: 1.4em; 120 | border: 0; 121 | outline: none; 122 | color: inherit; 123 | padding: 6px; 124 | border: 1px solid #999; 125 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 126 | -webkit-box-sizing: border-box; 127 | -moz-box-sizing: border-box; 128 | -ms-box-sizing: border-box; 129 | -o-box-sizing: border-box; 130 | box-sizing: border-box; 131 | -webkit-font-smoothing: antialiased; 132 | -moz-font-smoothing: antialiased; 133 | -ms-font-smoothing: antialiased; 134 | -o-font-smoothing: antialiased; 135 | font-smoothing: antialiased; 136 | } 137 | 138 | #new-todo { 139 | padding: 16px 16px 16px 60px; 140 | border: none; 141 | background: rgba(0, 0, 0, 0.02); 142 | z-index: 2; 143 | box-shadow: none; 144 | } 145 | 146 | #main { 147 | position: relative; 148 | z-index: 2; 149 | border-top: 1px dotted #adadad; 150 | } 151 | 152 | label[for='toggle-all'] { 153 | display: none; 154 | } 155 | 156 | #toggle-all { 157 | position: absolute; 158 | top: -56px; 159 | left: -15px; 160 | width: 65px; 161 | height: 41px; 162 | text-align: center; 163 | border: none; /* Mobile Safari */ 164 | -webkit-appearance: none; 165 | /*-moz-appearance: none;*/ 166 | -ms-appearance: none; 167 | -o-appearance: none; 168 | appearance: none; 169 | -webkit-transform: rotate(90deg); 170 | /*-moz-transform: rotate(90deg);*/ 171 | -ms-transform: rotate(90deg); 172 | /*-o-transform: rotate(90deg);*/ 173 | transform: rotate(90deg); 174 | } 175 | 176 | #toggle-all:before { 177 | content: '»'; 178 | font-size: 28px; 179 | color: #d9d9d9; 180 | padding: 0 25px 7px; 181 | } 182 | 183 | #toggle-all:checked:before { 184 | color: #737373; 185 | } 186 | 187 | #todo-list { 188 | margin: 0; 189 | padding: 0; 190 | list-style: none; 191 | } 192 | 193 | #todo-list li { 194 | position: relative; 195 | font-size: 24px; 196 | border-bottom: 1px dotted #ccc; 197 | } 198 | 199 | #todo-list li:last-child { 200 | border-bottom: none; 201 | } 202 | 203 | #todo-list li.editing { 204 | border-bottom: none; 205 | padding: 0; 206 | } 207 | 208 | #todo-list li.editing .edit { 209 | display: block; 210 | width: 506px; 211 | padding: 13px 17px 12px 17px; 212 | margin: 0 0 0 43px; 213 | } 214 | 215 | #todo-list li.editing .view { 216 | display: none; 217 | } 218 | 219 | #todo-list li .toggle { 220 | text-align: center; 221 | width: 40px; 222 | height: 40px; 223 | position: absolute; 224 | top: 0; 225 | bottom: 0; 226 | margin: auto 0; 227 | border: none; /* Mobile Safari */ 228 | -webkit-appearance: none; 229 | /*-moz-appearance: none;*/ 230 | -ms-appearance: none; 231 | -o-appearance: none; 232 | appearance: none; 233 | } 234 | 235 | #todo-list li .toggle:after { 236 | font-size: 18px; 237 | content: '✔'; 238 | line-height: 43px; /* 40 + a couple of pixels visual adjustment */ 239 | font-size: 20px; 240 | color: #d9d9d9; 241 | text-shadow: 0 -1px 0 #bfbfbf; 242 | } 243 | 244 | #todo-list li .toggle:checked:after { 245 | color: #85ada7; 246 | text-shadow: 0 1px 0 #669991; 247 | bottom: 1px; 248 | position: relative; 249 | } 250 | 251 | #todo-list li label { 252 | word-break: break-word; 253 | padding: 15px; 254 | margin-left: 45px; 255 | display: block; 256 | line-height: 1.2; 257 | -webkit-transition: color 0.4s; 258 | -moz-transition: color 0.4s; 259 | -ms-transition: color 0.4s; 260 | -o-transition: color 0.4s; 261 | transition: color 0.4s; 262 | } 263 | 264 | #todo-list li.completed label { 265 | color: #a9a9a9; 266 | text-decoration: line-through; 267 | } 268 | 269 | #todo-list li .destroy { 270 | display: none; 271 | position: absolute; 272 | top: 0; 273 | right: 10px; 274 | bottom: 0; 275 | width: 40px; 276 | height: 40px; 277 | margin: auto 0; 278 | font-size: 22px; 279 | color: #a88a8a; 280 | -webkit-transition: all 0.2s; 281 | -moz-transition: all 0.2s; 282 | -ms-transition: all 0.2s; 283 | -o-transition: all 0.2s; 284 | transition: all 0.2s; 285 | } 286 | 287 | #todo-list li .destroy:hover { 288 | text-shadow: 0 0 1px #000, 289 | 0 0 10px rgba(199, 107, 107, 0.8); 290 | -webkit-transform: scale(1.3); 291 | -moz-transform: scale(1.3); 292 | -ms-transform: scale(1.3); 293 | -o-transform: scale(1.3); 294 | transform: scale(1.3); 295 | } 296 | 297 | #todo-list li .destroy:after { 298 | content: '✖'; 299 | } 300 | 301 | #todo-list li:hover .destroy { 302 | display: block; 303 | } 304 | 305 | #todo-list li .edit { 306 | display: none; 307 | } 308 | 309 | #todo-list li.editing:last-child { 310 | margin-bottom: -1px; 311 | } 312 | 313 | #footer { 314 | color: #777; 315 | padding: 0 15px; 316 | position: absolute; 317 | right: 0; 318 | bottom: -31px; 319 | left: 0; 320 | height: 20px; 321 | z-index: 1; 322 | text-align: center; 323 | } 324 | 325 | #footer:before { 326 | content: ''; 327 | position: absolute; 328 | right: 0; 329 | bottom: 31px; 330 | left: 0; 331 | height: 50px; 332 | z-index: -1; 333 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3), 334 | 0 6px 0 -3px rgba(255, 255, 255, 0.8), 335 | 0 7px 1px -3px rgba(0, 0, 0, 0.3), 336 | 0 43px 0 -6px rgba(255, 255, 255, 0.8), 337 | 0 44px 2px -6px rgba(0, 0, 0, 0.2); 338 | } 339 | 340 | #todo-count { 341 | float: left; 342 | text-align: left; 343 | } 344 | 345 | #filters { 346 | margin: 0; 347 | padding: 0; 348 | list-style: none; 349 | position: absolute; 350 | right: 0; 351 | left: 0; 352 | } 353 | 354 | #filters li { 355 | display: inline; 356 | } 357 | 358 | #filters li a { 359 | color: #83756f; 360 | margin: 2px; 361 | text-decoration: none; 362 | } 363 | 364 | #filters li a.selected { 365 | font-weight: bold; 366 | } 367 | 368 | #clear-completed { 369 | float: right; 370 | position: relative; 371 | line-height: 20px; 372 | text-decoration: none; 373 | background: rgba(0, 0, 0, 0.1); 374 | font-size: 11px; 375 | padding: 0 10px; 376 | border-radius: 3px; 377 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2); 378 | } 379 | 380 | #clear-completed:hover { 381 | background: rgba(0, 0, 0, 0.15); 382 | box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3); 383 | } 384 | 385 | #info { 386 | margin: 65px auto 0; 387 | color: #a6a6a6; 388 | font-size: 12px; 389 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7); 390 | text-align: center; 391 | } 392 | 393 | #info a { 394 | color: inherit; 395 | } 396 | 397 | #noscript { 398 | color: white; 399 | background: red; 400 | width: 100%; 401 | text-align: center; 402 | } 403 | 404 | #noscript p { 405 | margin: 0px; 406 | padding: 15px; 407 | font-size: large; 408 | font-weight: bold; 409 | } 410 | 411 | /* 412 | Hack to remove background from Mobile Safari. 413 | Can't use it globally since it destroys checkboxes in Firefox and Opera 414 | */ 415 | @media screen and (-webkit-min-device-pixel-ratio:0) { 416 | #toggle-all, 417 | #todo-list li .toggle { 418 | background: none; 419 | } 420 | } 421 | 422 | .hidden{ 423 | display:none; 424 | } 425 | -------------------------------------------------------------------------------- /async-webinar/src/demo/async_webinar.cljs: -------------------------------------------------------------------------------- 1 | (ns demo.async-webinar 2 | (:require 3 | [hoplon.core :as h] 4 | [hoplon.jquery] 5 | [javelin.core :refer [cell cell= defc defc= dosync]])) 6 | 7 | (defn mouse-loc->vec 8 | "Given a Google Closure normalized DOM mouse event return the 9 | mouse x and y position as a two element vector." 10 | [e] 11 | [(.-clientX e) (.-clientY e)]) 12 | 13 | ;; ============================================================================= 14 | ;; Example 1 15 | 16 | (defc ex1-content ["Waiting for a click ...."]) 17 | (defc ex1-click-count 0) 18 | (defn ex1 [] 19 | (when (< @ex1-click-count 1) 20 | (swap! ex1-click-count inc) 21 | (swap! ex1-content conj "Got a click!"))) 22 | 23 | ;; ============================================================================= 24 | ;; Example 2 25 | 26 | (defc ex2-content ["Waiting for a click ...."]) 27 | (defc ex2-click-count 0) 28 | (defn ex2 [] 29 | (when (= @ex2-click-count 1) 30 | (swap! ex2-click-count inc) 31 | (swap! ex2-content conj "Done")) 32 | (when (= @ex2-click-count 0) 33 | (swap! ex2-click-count inc) 34 | (swap! ex2-content conj "Got a Click!" "Waiting for another click ...."))) 35 | 36 | ;; ============================================================================= 37 | ;; Example 3 38 | 39 | (defc ex3-content ["Waiting for a click from Button A ....."]) 40 | (defc ex3-click-count-a 0) 41 | (defc ex3-click-count-b 0) 42 | (defn ex3a [] 43 | (when (= @ex3-click-count-a 0) 44 | (swap! ex3-click-count-a inc) 45 | (swap! ex3-content conj "Got a click!" "Waiting for a click from Button B ....")) ) 46 | (defn ex3b [] 47 | (when (and (= @ex3-click-count-a 1) (= @ex3-click-count-b 0)) 48 | (swap! ex3-click-count-b inc) 49 | (swap! ex3-content conj "Done!"))) 50 | 51 | ;; ============================================================================= 52 | ;; Example 6 53 | 54 | (defc ex6-content ["Click the button to start tracking the mouse."]) 55 | (defc ex6-button-name "GO!") 56 | (defn ex6-toggle [] 57 | (let [new-name (if (= @ex6-button-name "GO!") "STOP!" "GO!")] 58 | (reset! ex6-button-name new-name))) 59 | (defn ex6 [e] 60 | (when (= @ex6-button-name "STOP!") 61 | (swap! ex6-content conj (str (mouse-loc->vec e))))) 62 | 63 | ;; ============================================================================= 64 | ;; Example 7 65 | 66 | (defc ex7-content ["Click the button to start tracking the mouse."]) 67 | (defc ex7-button-name "GO!") 68 | (defn ex7-toggle [] 69 | (let [new-name (if (= @ex7-button-name "GO!") "STOP!" "GO!")] 70 | (reset! ex7-button-name new-name))) 71 | (defn ex7 [e] 72 | (when (= @ex7-button-name "STOP!") 73 | (let [[_x y :as m] (mouse-loc->vec e)] 74 | (when (zero? (mod y 5)) 75 | (swap! ex7-content conj (str m)))))) 76 | 77 | ;; ============================================================================= 78 | ;; Example 8 79 | 80 | (defc ex8-content ["Click the button ten times."]) 81 | (defc ex8-click-count 0) 82 | (defn ex8 [] 83 | (when (< @ex8-click-count 10) 84 | (swap! ex8-click-count inc) 85 | (when (= @ex8-click-count 1) 86 | (swap! ex8-content conj "1 Click!")) 87 | (when (> @ex8-click-count 1) 88 | (swap! ex8-content conj (str @ex8-click-count " clicks!"))) 89 | (when (= @ex8-click-count 10) 90 | (swap! ex8-content conj "Done.")))) 91 | 92 | ;; ============================================================================= 93 | ;; Example 9 94 | 95 | (defc ex9-index 0) 96 | (defc ex9-animals [:aardvark :beetle :cat :dog :elk :ferret 97 | :goose :hippo :ibis :jellyfish :kangaroo]) 98 | (defc= ex9-card (nth ex9-animals ex9-index)) 99 | (defn ex9-prev [] 100 | (when (> @ex9-index 0) 101 | (swap! ex9-index dec))) 102 | (defn ex9-next [] 103 | (when (< @ex9-index (dec (count @ex9-animals))) 104 | (swap! ex9-index inc))) 105 | 106 | ;; ============================================================================= 107 | ;; Example 10 108 | 109 | (defc ex10-button-name "START!") 110 | (defc ex10-index 0) 111 | (defn ex10 [] 112 | (let [the-name @ex10-button-name] 113 | (when (= the-name"START!") 114 | (reset! ex10-button-name "STOP!")) 115 | (when (= the-name"STOP!") 116 | (reset! ex10-button-name "DONE!")))) 117 | (defc ex10-animals [:aardvark :beetle :cat :dog :elk :ferret 118 | :goose :hippo :ibis :jellyfish :kangaroo]) 119 | (defc= ex10-max (dec (count ex10-animals))) 120 | (defc= ex10-card (nth ex10-animals ex10-index)) 121 | (defn ex10-prev [] 122 | (if (> @ex10-index 0) 123 | (swap! ex10-index dec) 124 | (reset! ex10-index @ex10-max))) 125 | (defn ex10-next [] 126 | (if (< @ex10-index @ex10-max) 127 | (swap! ex10-index inc) 128 | (reset! ex10-index 0))) 129 | (defn ex10-nav [k] 130 | (when (= @ex10-button-name "STOP!") 131 | (when (= k :next) 132 | (ex10-next)) 133 | (when (= k :prev) 134 | (ex10-prev)))) 135 | 136 | (defn ex10-keys [e] 137 | (when (= @ex10-button-name "STOP!") 138 | (when (= (.-keyCode e) 39) (ex10-nav :next)) 139 | (when (= (.-keyCode e) 37) (ex10-nav :prev)))) 140 | 141 | 142 | (h/defelem async-webinar [] 143 | (h/div 144 | (h/