The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .gitignore
├── CHANGES.md
├── README.md
├── README.old.md
├── bin
    ├── classpath
    ├── run
    └── test
├── boot.properties
├── build.boot
├── circle.yml
├── epl.html
├── examples
    ├── animation
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── counters
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── cursor_as_key
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── harmful
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── hello
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── input
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── instrument
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── mixins
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── mouse
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── multi
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── multiroot
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── refs
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── shared
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── sortable
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── state_bug
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── stateful
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── two_lists
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── typeahead
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── unmount
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    ├── update_props
    │   ├── index.html
    │   └── src
    │   │   └── core.cljs
    └── verify
    │   ├── index.html
    │   └── src
    │       └── core.cljs
├── pom.xml
├── project.clj
├── resources
    ├── index.html
    └── public
    │   └── devcards
    │       ├── index.html
    │       └── main.cljs.edn
├── script
    ├── build.clj
    ├── figwheel.clj
    ├── index.html
    ├── repl.clj
    ├── test.clj
    └── watch.clj
└── src
    ├── devcards
        └── om
        │   └── devcards
        │       ├── autocomplete.cljs
        │       ├── bugs.cljs
        │       ├── core.cljs
        │       ├── shared_fn_test.cljs
        │       ├── tutorials.cljs
        │       └── utils.cljs
    ├── main
        ├── data_readers.clj
        ├── deps.cljs
        └── om
        │   ├── checksums.clj
        │   ├── core.clj
        │   ├── core.cljs
        │   ├── dom.cljc
        │   ├── dom.cljs
        │   ├── externs.js
        │   ├── impl.cljs
        │   ├── next.cljc
        │   ├── next
        │       ├── cache.cljs
        │       ├── impl
        │       │   └── parser.cljc
        │       ├── protocols.cljc
        │       └── server.clj
        │   ├── tempid.cljc
        │   ├── transit.cljc
        │   └── util.cljc
    └── test
        └── om
            ├── checksums_test.clj
            ├── dom_test.clj
            ├── next
                ├── next_test.clj
                ├── run_tests.cljs
                ├── tests.cljc
                └── tutorials_test.clj
            ├── test_utils.clj
            └── tests.cljs


/.gitignore:
--------------------------------------------------------------------------------
 1 | out
 2 | /out
 3 | /target
 4 | /classes
 5 | /checkouts
 6 | pom.xml.asc
 7 | *.map
 8 | *.js
 9 | *.jar
10 | *.class
11 | /.lein-*
12 | /.nrepl-port
13 | examples/history
14 | *init.clj
15 | .repl*
16 | .idea
17 | om.iml
18 | node_modules
19 | .cljs_node_repl


--------------------------------------------------------------------------------
/CHANGES.md:
--------------------------------------------------------------------------------
  1 | ## 0.8.8
  2 | 
  3 | ### Changes
  4 | * Leverage 0.0-2755 macro usage enhancements
  5 | 
  6 | ## 0.8.7
  7 | 
  8 | ### Changes
  9 | * Rely on newer cljsjs.react artifact
 10 | 
 11 | ## 0.8.6
 12 | 
 13 | ### Changes
 14 | * Rely on cljsjs.react artifact
 15 | 
 16 | ## 0.8.5
 17 | 
 18 | ### Fixes
 19 | * Fix subtle rendering bug in code set-state! queue logic
 20 | 
 21 | ## 0.8.4
 22 | 
 23 | ### Changes
 24 | * Bump dependencies
 25 | 
 26 | ## 0.8.3
 27 | 
 28 | ### Changes
 29 | * Bump dependencies
 30 | 
 31 | ## 0.8.2
 32 | 
 33 | ### Changes
 34 | * Bump dependencies
 35 | 
 36 | ## 0.8.1
 37 | 
 38 | ### Changes
 39 | * Proper fix to OM-300
 40 | * Bump React JAR dep to 0.12.2.2
 41 | 
 42 | ## 0.8.0
 43 | 
 44 | ### Changes
 45 | * OM-307: get-props extended to have a two arity form (korks)
 46 | * OM-306: dissoc :raf
 47 | 
 48 | ## 0.8.0-rc1
 49 | 
 50 | ### Changes
 51 | * relax transact! precondition, only require ITransact instance
 52 | 
 53 | ### Fixes
 54 | * OM-301: Deprecation warning for om.dom/input
 55 | * OM-300: do not preserve component local state entry after unmount
 56 | 
 57 | ## 0.8.0-beta5
 58 | 
 59 | ### Changes
 60 | * Depend on 0.12.2.1 React JAR with new externs
 61 | 
 62 | ## 0.8.0-beta4
 63 | 
 64 | ### Changes
 65 | * Add low-level namespace om.impl
 66 | * update to React 0.12.2
 67 | 
 68 | ### Fixes
 69 | * OM-190: add :key-fn option to `om.core/build`
 70 | * OM-253: overrideable rAF
 71 | * OM-294: IKVReduce for MapCursor & IndexedCursor
 72 | * OM-296: `no-local-merge-pending-state` was not setting `:previous-state`
 73 | 
 74 | ## 0.8.0-beta3
 75 | 
 76 | ### Fixes
 77 | * OM-290: component? precondition unexpectedly failing
 78 | 
 79 | ## 0.8.0-beta2
 80 | 
 81 | ### Enhancements
 82 | * Preconditions on most of the public api to support earlier failures
 83 | * Make `render-all` public
 84 | 
 85 | ## 0.8.0-beta1
 86 | 
 87 | ### Enhancements
 88 | * Improved multimethod support, mount/unmount life-cycle methods invoked
 89 | as expected
 90 | 
 91 | ## 0.8.0-alpha2
 92 | 
 93 | ### Enhancements
 94 | * OM-260: remove cursor consistency checks
 95 | 
 96 | ### Fixes
 97 | * OM-276: bad pending state for no local state
 98 | * OM-262: input behavior regression
 99 | * OM-270: incorrect no-local state behavior
100 | * OM-274: incorrect -lookup behavior for IndexedCursor
101 | * OM-267: bad logic for not found case in MapCusor -lookup implementation
102 | * OM-271: typo, parent not passed to ref sub-cursor
103 | 
104 | ## 0.8.0-alpha1
105 | 
106 | ### Enhancements
107 | * reference cursors
108 | * om.core/commit!, like om.core/transact! but will not trigger a re-render
109 | * add marquee tag
110 | * add om.core/mounted?
111 | * experimental support to write component local state into global state
112 | 
113 | ### Changes
114 | * React 0.11.2
115 | 
116 | ### Fixes
117 | * om.core/root now properly returns mounted component
118 | * default shouldComponentUpdate now compares state
119 | 
120 | ## 0.7.3
121 | 
122 | ### Changes
123 | * OM-243: fix regression in supporting components built from non-cursors
124 | 
125 | ## 0.7.2
126 | 
127 | ### Changes
128 | * OM-239: update components if cursor path changes
129 | 
130 | ## 0.7.1
131 | 
132 | ### Changes
133 | * OM-133: validate Om component fn return values
134 | * OM-134: add om.core/set-state-nr! and om.core/update-state-nr!, they do not refresh (experimental)
135 | * OM-162: extend default cursor to IEmptyableCollection (experimental)
136 | * OM-180: add om.core/detach-root to remove Om render loop
137 | * OM-214: Cursors should implement IHash
138 | 
139 | ## 0.7.0
140 | 
141 | ### Changes
142 | * BREAKING: :ctor options deprecated for :descriptor see docs
143 | * depend on ClojureScript 0.0-2277
144 | * depend on React 0.11.1
145 | 
146 | Fixes
147 | ###
148 | * OM-170: :tx-listen registration issues
149 | 
150 | ## 0.6.5
151 | 
152 | ### Changes
153 | * depend on Clojure 1.6.0 and ClojureScript 0.0-2268
154 | 
155 | ## 0.6.4
156 | 
157 | ### Changes
158 | * depend on 0.9.0.2 React CLJS
159 | 
160 | ## 0.6.3
161 | 
162 | ### Enhancements
163 | * added ellipse tag
164 | 
165 | ### Bug Fixes
166 | * OM-179: prevent forceUpdate() if component not mounted
167 | * OM-175: error on render-to-str with set-state!
168 | 
169 | ## 0.6.2
170 | 
171 | ### Bug Fixes
172 | * Stale code around component local state
173 | 
174 | ## 0.6.1
175 | 
176 | ### Enhancements
177 | * Pure shouldComponentUpdate logic now uses equiv instead of identical?
178 | * add om.core/rendering? predicate to detect React render phase
179 | * more sensible handling of component local state w/o internal graft,
180 |   we now rely on React's forceUpdate
181 | * add ICursorDerive protocol
182 | 
183 | ### Bug Fixes
184 | * OM-155: multiroot and & :tx-listen incompatible
185 | * OM-152: pass ::index to :fn if available
186 | * OM-150: add missing SVG tags
187 | 
188 | ## 0.6.0
189 | 
190 | ### Breaking Changes
191 | * OM-152: eliminate om.core/graft
192 | 
193 | ### Changes
194 | * added ICursorDerive protocol
195 | * document :ctor option to om.core/build
196 | * add om.core/rendering? predicate
197 | * pass ::index to :fn if available in om.core/build
198 | 
199 | ### Fixes
200 | * OM-150: missing SVG tags
201 | * OM-155: multiroot & :tx-listen incompatible
202 | 
203 | ## 0.5.3
204 | 
205 | ### Changes
206 | * added pure-methods for reuse
207 | * added specify-state-methods! for reuse
208 | * can explicitly give a component an id via :om.core/id with :init-state
209 | 
210 | ## 0.5.2
211 | 
212 | ### Enhancements
213 | * OM-127: om.core/refresh!
214 | * OM-28: om.core/update-state!
215 | * protocols for local state
216 | * Pure implementation methods now provided in immutable map
217 | 
218 | ## 0.5.1
219 | 
220 | ### Changes
221 | * Requires ClojureScript 0.0-2173
222 | * IOmSwap protocol for app state representations besides standard atom
223 | * :instrument option for om.core/root
224 | * Add IDisplayName to support React Chrome Dev Tools
225 | * om.core/build takes :ctor option to provide React backing component
226 |   class besides om.core/Pure
227 | 
228 | ### Fixes
229 | * OM-122: IWillReceiveProps protocol
230 | 
231 | ## 0.5.0
232 | 
233 | ### Changes
234 | * Upgrade to React 0.9.0
235 | 
236 | ## 0.5.0-rc1
237 | 
238 | ### Breaking Changes
239 | * IDidMount, IDidUpdate no longer take node to match React 0.9.0-rc1
240 | 
241 | ### Enhancements
242 | * om.dom/render-to-str added
243 | * OM-72: :path option for om.core/root
244 | 
245 | ## 0.4.2
246 | 
247 | ### Enhancements
248 | * OM-112: transact! special cases when korks is nil
249 | 
250 | ### Bug fixes
251 | * OM-114: om.core/update! doesn't pass tag
252 | 
253 | ## 0.4.1
254 | 
255 | ### Bug fixes
256 | * OM-110: transact! broken for empty path
257 | 
258 | ## 0.4.0
259 | 
260 | ### Breaking Changes
261 | * `om.core/root` signature changed to be more like `om.core/build`
262 | * `om.core/update!` no longer takes keys, or function, or function args
263 | * `om.core/transact!` no longer takes arguments after function
264 | 
265 | ### Enhancements
266 | * `om.core/transact!` now takes optional 4th argument, a tag
267 | * `om.core/update!` now takes optional 4th argument, a tag
268 | * `om.core/root` now supports `:tx-listen` option to observe all transactions
269 | 
270 | ### Bug fixes
271 | * `om.core/shared` was brittle, now works without cursors
272 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # Om
 2 | 
 3 | > *NOTE*: This project is no longer under active development. If you'd like to use
 4 | > a library that's well maintained that was inspired by some of the ideas
 5 | > presented here see [Fulcro](https://github.com/fulcrologic/fulcro)
 6 | 
 7 | A [ClojureScript](http://github.com/clojure/clojurescript) UI framework and
 8 | client/server architecture over [Facebook's
 9 | React](http://facebook.github.io/react/).
10 | 
11 | Om UIs are out of the box snapshotable and undoable and these operations have 
12 | no implementation complexity and little overhead.
13 | 
14 | Om borrows ideas liberally from [Facebook's
15 | Relay](https://facebook.github.io/relay/) and [Netflix's
16 | Falcor](http://netflix.github.io/falcor/) with a dash of inspiration from
17 | [Datomic pull syntax](http://docs.datomic.com/pull.html) to avoid the typical 
18 | incidental complexity that arises from client/server state management.
19 | 
20 | ## Dependency Information
21 | 
22 | Latest release: 1.0.0-beta1
23 | 
24 | [Leiningen](http://github.com/technomancy/leiningen/) and [Boot](http://boot-clj.com) 
25 | dependency information:
26 | 
27 | ```
28 | [org.omcljs/om "1.0.0-beta1"]
29 | ```
30 | 
31 | [Maven](http://maven.apache.org) dependency information:
32 | 
33 | ```
34 | <dependency>
35 |   <groupId>org.omcljs</groupId>
36 |   <artifactId>om</artifactId>
37 |   <version>1.0.0-beta1</version>
38 | </dependency>
39 | ```
40 | 
41 | ## Example
42 | 
43 | ```clojure
44 | (ns example
45 |   (:require [goog.dom :as gdom]
46 |             [om.dom :as dom]
47 |             [om.next :as om :refer [defui]]))
48 | 
49 | (defui Hello
50 |   Object
51 |   (render [this]
52 |     (dom/h1 nil "Hello, world!")))
53 | 
54 | (def hello (om/factory Hello))
55 | 
56 | (.render js/ReactDOM (hello) (gdom/getElement "example"))
57 | ```
58 | 
59 | ## Tutorials
60 | 
61 | There is an Quick Start tutorial that will introduce you to the core
62 | concepts of Om
63 | [here](https://github.com/omcljs/om/wiki/Quick-Start-%28om.next%29). There are
64 | also a variety of other guides [here](https://github.com/omcljs/om/wiki#om-next).
65 | 
66 | ## Documentation
67 | 
68 | There is documentation [here](https://github.com/omcljs/om/wiki/Documentation-%28om.next%29)
69 | 
70 | ## Contributing
71 | 
72 | Please contact me via email to request an electronic Contributor
73 | Agreement. Once your electronic CA has been signed and returned to me
74 | I will accept pull requests.
75 | 
76 | ## Community
77 | 
78 | If you are looking for help please get in touch either on the 
79 | [clojurians.slack.com **#om** channel](http://clojurians.net) or the 
80 | [om-cljs Google Group](https://groups.google.com/d/forum/om-cljs).  
81 | 
82 | ## References
83 | 
84 | * [Worlds: Controlling the Scope of Side Effects](http://www.vpri.org/pdf/tr2011001_final_worlds.pdf)
85 | * [A Functional I/O System](http://www.ccs.neu.edu/racket/pubs/icfp09-fffk.pdf)
86 | * [Directness and Liveness in the Morphic User Interface Construction Environment](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103.600&rep=rep1&type=pdf)
87 | * [Learnable Programming](http://worrydream.com/LearnableProgramming/)
88 | * [Relay](https://facebook.github.io/relay/)
89 | * [Falcor](http://netflix.github.io/falcor/)
90 | * [GraphQL](http://graphql.org)
91 | * [Datomic pull syntax](http://docs.datomic.com/pull.html)
92 | 
93 | ## Copyright and license
94 | 
95 | Copyright © 2013-2017 David Nolen
96 | 
97 | Licensed under the EPL (see the file epl.html).
98 | 


--------------------------------------------------------------------------------
/README.old.md:
--------------------------------------------------------------------------------
  1 | # Om
  2 | 
  3 | A [ClojureScript](http://github.com/clojure/clojurescript) interface
  4 | to [Facebook's React](http://facebook.github.io/react/).
  5 | 
  6 | Om allows users to represent their UIs simply as
  7 | [EDN](http://github.com/edn-format/edn). Because ClojureScript data is
  8 | immutable data, Om can always rapidly re-render the UI from the
  9 | root. Thus Om UIs are out of the box snapshotable and undoable and
 10 | these operations have no implementation complexity and little
 11 | overhead.
 12 | 
 13 | [See](http://swannodette.github.io/todomvc/labs/architecture-examples/om-undo/index.html)
 14 | for [yourself](http://swannodette.github.io/2013/12/31/time-travel).
 15 | 
 16 | ## Unique Features
 17 | 
 18 | Om supports features not currently present in React:
 19 | 
 20 | * Global state management facilities built in
 21 | * Components may have arbitrary data dependencies, not limited to props & state
 22 | * Component construction can be intercepted via
 23 |   `:instrument`. Simplifies debugging components and generic editors.
 24 | * Provides stream of all application state change deltas via
 25 |   `:tx-listen`. Simplifies synchronization online and offline.
 26 | * Customizable semantics. Fine grained control over how components store
 27 |   state, even for components outside of your control. Simplifies using
 28 |   Om components outside the Om framework, debugging, and adding event
 29 |   hooks not anticipated by original component designer.
 30 | 
 31 | ## Tutorials
 32 | 
 33 | There is an in-depth tutorial that will introduce you to the core
 34 | concepts of Om
 35 | [here](http://github.com/swannodette/om/wiki/Basic-Tutorial) and a
 36 | real-world integration example
 37 | [here](http://github.com/swannodette/om/wiki/Intermediate-Tutorial). The
 38 | community maintained [om-cookbook](https://github.com/omcljs/om-cookbook)
 39 | covers many common idioms and patterns.
 40 | 
 41 | ## Examples
 42 | 
 43 | ```clojure
 44 | (ns example
 45 |   (:require [om.core :as om]
 46 |             [om.dom :as dom]))
 47 | 
 48 | (defn widget [data owner]
 49 |   (reify
 50 |     om/IRender
 51 |     (render [this]
 52 |       (dom/h1 nil (:text data)))))
 53 | 
 54 | (om/root widget {:text "Hello world!"}
 55 |   {:target (. js/document (getElementById "my-app"))})
 56 | ```
 57 | 
 58 | The repo includes several simple examples you can build yourself. If
 59 | you view the `project.clj` you will see their build
 60 | identifiers. Assuming you have [Leiningen](http://leiningen.org/)
 61 | installed, to build an example run:
 62 | 
 63 | ```
 64 | lein cljsbuild once <build-id>
 65 | ```
 66 | 
 67 | Then open the corresponding `index.html` in your favorite browser.
 68 | 
 69 | For a more fleshed-out example, please see the Om implementation of
 70 | [TodoMVC](http://todomvc.com)
 71 | [exists here](http://github.com/swannodette/todomvc/blob/gh-pages/labs/architecture-examples/om/src/todomvc/app.cljs).
 72 | 
 73 | ## Documentation
 74 | 
 75 | There is documentation [here](http://github.com/swannodette/om/wiki/Documentation).
 76 | 
 77 | There is also a
 78 | [conceptual overview](http://github.com/swannodette/om/wiki/Conceptual-overview)
 79 | that we recommend reading as there are some design choices in Om that
 80 | make it quite different from other client side solutions and even
 81 | React itself.
 82 | 
 83 | ## Reusable Components
 84 | 
 85 | Om emphasizes building modular and adaptable components. Some
 86 | examples:
 87 | 
 88 | * [om-bootstrap](https://github.com/racehub/om-bootstrap), Bootstrap 3 Om Components
 89 | * [ankha](http://github.com/noprompt/ankha), an EDN inspector view
 90 | * [om-draggable](https://github.com/sgrove/om-draggable), generic
 91 |   draggable
 92 | * [om-autocomplete](https://github.com/arosequist/om-autocomplete),
 93 |   customizable autocompleter
 94 | * [ff-om-draggable](https://github.com/neo/ff-om-draggable)
 95 | * [om-widgets](https://bitbucket.org/athieme/om-widgets)
 96 | * [om-dev-component](https://github.com/ioRekz/om-dev-component), add dev features (e.g. state history navigation) to your component
 97 | * [om-sync](http://github.com/swannodette/om-sync), keep client and
 98 |   server in sync (experimental)
 99 | 
100 | ## Applications built with Om
101 | 
102 | * [Project FiFo](https://blog.project-fifo.net/the-stack-we-choose-erlang-smartos-clojure/), a SmartOS cloud orchestration platform
103 | * [Recurse Center Community](https://github.com/hackerschool/community)
104 | * [Framed](http://www.framed.io/)
105 | * [Netrunner](https://github.com/mtgred/netrunner)
106 | * [CircleCI](http://www.circleci.com/), source [here](https://github.com/circleci/frontend)
107 | * [Precursor](https://precursorapp.com)
108 | * [Assistant](https://github.com/29decibel/assistant)
109 | * [Fitsme](http://fitsmeapp.com)
110 | * [Goya](http://jackschaedler.github.io/goya/), pixel editor with
111 |   undo/redo and visual history
112 | * [AppShare](https://github.com/zubairq/AppShare), a Clojure web framework
113 | * [wordsmith](http://wordsmith.variadic.me), a markdown editor
114 | * [omchaya](http://github.com/sgrove/omchaya)
115 | * [BVCA Private Equity Map](http://bvca.clustermap.trampolinesystems.com/)
116 | * [session](http://github.com/kovasb/session)
117 | * [pOModoro](http://pomodoro.trevorlandau.net)
118 | * [Dakait](http://github.com/verma/dakait), a web-based tool to manage
119 |   downloads
120 | * [Mega Super Mario World](http://github.com/city41/mario-review), a detailed review of the classic video game and a SNES video editor
121 | * [Time for Coffee!](http://www.timeforcoffee.ch), a handy website to display the next departures at public transport stops in Switzerland
122 | * [Omingard](https://omingard.5apps.com), a Solitaire-like card game. Making-of: [My Way into Clojure](http://www.railslove.com/stories/my-way-into-clojure-building-a-card-game-with-om-part-1).
123 | * [Horizon Alpha](https://github.com/BertrandDechoux/horizon-alpha), a quick Hack and slash game using the Noob universe
124 | * [Solari Architects](http://solariarchitects.com/), portfolio for architecture firm.  
125 | 
126 | ## Using it
127 | 
128 | The current version depends on React 0.13.3.
129 | 
130 | Make sure you have [Leiningen](http://leiningen.org/) installed.
131 | 
132 | Your `project.clj` should include something like the following:
133 | 
134 | ```clojure
135 | (defproject foo "0.1.0"
136 |   ...
137 |   :dependencies [[org.clojure/clojure "1.6.0"]
138 |                  [org.clojure/clojurescript "0.0-2760"]
139 |                  [org.omcljs/om "0.9.0"]]
140 |   ...)
141 | ```
142 | 
143 | ### React with Add-Ons
144 | 
145 | If you would rather use React with Add-Ons you can configure this
146 | with Maven's exclusions feature:
147 | 
148 | ```clojure
149 | (defproject foo "0.1.0"
150 |   ...
151 |   :dependencies [[org.clojure/clojure "1.6.0"]
152 |                  [org.clojure/clojurescript "0.0-2760"]
153 |                  [org.omcljs/om "0.9.0" :exclusions [cljsjs/react]]
154 |                  [cljsjs/react-with-addons "0.13.3-0"]]
155 |   ...)
156 | ```
157 | 
158 | ### Build configuration
159 | 
160 | For local development your
161 | [lein-cljsbuild](http://github.com/emezeske/lein-cljsbuild) settings
162 | should look something like this:
163 | 
164 | ```clojure
165 | :cljsbuild {
166 |   :builds [{:id "dev"
167 |             :source-paths ["src"]
168 |             :compiler {
169 |               :main main.core
170 |               :output-to "main.js"
171 |               :output-dir "out"
172 |               :optimizations :none
173 |               :source-map true}}]}
174 | ```
175 | 
176 | Your markup should look something like the following:
177 | 
178 | ```html
179 | <html>
180 |     <body>
181 |        <div id="my-app"></div>
182 |        <script src="main.js" type="text/javascript"></script>
183 |     </body>
184 | </html>
185 | ```
186 | 
187 | For production your [lein-cljsbuild](http://github.com/emezeske/lein-cljsbuild) settings should look something
188 | like this:
189 | 
190 | ```clojure
191 | :cljsbuild {
192 |   :builds [{:id "release"
193 |             :source-paths ["src"]
194 |             :compiler {
195 |               :main main.core
196 |               :output-to "main.js"
197 |               :optimizations :advanced
198 |               :pretty-print false}}]}
199 | ```
200 | 
201 | ## Contributing
202 | 
203 | Please contact me via email to request an electronic Contributor
204 | Agreement. Once your electronic CA has been signed and returned to me
205 | I will accept pull requests.
206 | 
207 | ## Community
208 | 
209 | If you are looking for help please get in touch either on the 
210 | [clojurians.slack.com **#om** channel](http://clojurians.net) or the 
211 | [om-cljs Google Group](https://groups.google.com/d/forum/om-cljs).  
212 | 
213 | ## FAQ
214 | 
215 | ### Can I use a different HTML Syntax?
216 | 
217 | Om is not opinionated about HTML syntax, third parties can provide the
218 | preferred flavors over the `React.DOM` api. Alternative syntaxes will
219 | be listed here:
220 | 
221 | * [sablono](http://github.com/r0man/sablono), Hiccup-style
222 | * [kioo](http://github.com/ckirkendall/kioo), Enlive-style
223 | 
224 | ### Does Om provide routing?
225 | 
226 | Om does not ship with a router and is unlikely to. However
227 | ClojureScript routing libraries exist that handle this problem quite
228 | well:
229 | 
230 | * [secretary](http://github.com/gf3/secretary)
231 | * [silk](http://github.com/DomKM/silk)
232 | * [bidi](http://github.com/juxt/bidi)
233 | 
234 | ### How do I test Om programs?
235 | 
236 | * Sean Grove's [omchaya](http://github.com/sgrove/omchaya) is a good
237 |   starting point for understanding common testing patterns
238 | * There are some notes [here](http://github.com/swannodette/om/wiki/Testing)
239 | 
240 | ## References
241 | 
242 | * [Worlds: Controlling the Scope of Side Effects](http://www.vpri.org/pdf/tr2011001_final_worlds.pdf)
243 | * [A Functional I/O System](http://www.ccs.neu.edu/racket/pubs/icfp09-fffk.pdf)
244 | * [Directness and Liveness in the Morphic User Interface Construction Environment](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.103.600&rep=rep1&type=pdf)
245 | * [Learnable Programming](http://worrydream.com/LearnableProgramming/)
246 | 
247 | ## Copyright and license
248 | 
249 | Copyright © 2013-2017 David Nolen
250 | 
251 | Licensed under the EPL (see the file epl.html).
252 | 


--------------------------------------------------------------------------------
/bin/classpath:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | 
 3 | set -e
 4 | 
 5 | classpath_file=classpath.txt
 6 | 
 7 | mvn -q dependency:build-classpath -Dmdep.outputFile=$classpath_file
 8 | 
 9 | (echo ":`pwd`/src/main:`pwd`/src/test:`pwd`/src/resources:`pwd`/target/classes") >> $classpath_file
10 | 


--------------------------------------------------------------------------------
/bin/run:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | set -e
4 | 
5 | java -cp `cat classpath.txt` clojure.main $@


--------------------------------------------------------------------------------
/bin/test:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | 
3 | set -e
4 | 
5 | bin/classpath
6 | java -cp `cat classpath.txt` clojure.main script/test.clj
7 | node target/test/test.js


--------------------------------------------------------------------------------
/boot.properties:
--------------------------------------------------------------------------------
1 | #http://boot-clj.com
2 | #Fri Sep 02 22:34:38 WEST 2016
3 | BOOT_CLOJURE_NAME=org.clojure/clojure
4 | BOOT_CLOJURE_VERSION=1.9.0-alpha19
5 | BOOT_VERSION=2.7.2
6 | 


--------------------------------------------------------------------------------
/build.boot:
--------------------------------------------------------------------------------
  1 | (set-env!
  2 |  :source-paths    #{"src/main"}
  3 |  :dependencies '[[org.clojure/clojure         "1.9.0-alpha19"  :scope "provided"]
  4 |                  [org.clojure/clojurescript   "1.9.908"        :scope "provided"
  5 |                   :classifier "aot"
  6 |                   :exclusions [org.clojure/clojure
  7 |                                org.clojure/data.json]]
  8 |                  [org.clojure/clojurescript   "1.9.908"        :scope "provided"
  9 |                   :exclusions [org.clojure/clojure
 10 |                                org.clojure/data.json]]
 11 |                  [org.clojure/data.json       "0.2.6"          :scope "provided"
 12 |                   :classifier "aot"]
 13 |                  [cljsjs/react                "16.0.0-0"]
 14 |                  [cljsjs/react-dom            "16.0.0-0"]
 15 |                  [com.cognitect/transit-clj   "0.8.300"]
 16 |                  [com.cognitect/transit-cljs  "0.8.239"]
 17 | 
 18 |                  [org.clojure/core.async      "0.3.443"        :scope "test"
 19 |                   :exclusions [org.clojure/tools.reader]]
 20 |                  [figwheel-sidecar            "0.5.10"         :scope "test"
 21 |                   :exclusions [org.clojure/clojurescript
 22 |                                org.clojure/tools.reader]]
 23 |                  [devcards                    "0.2.4-SNAPSHOT" :scope "test"
 24 |                   :exclusions [org.clojure/clojurescript]]
 25 |                  [com.cemerick/piggieback     "0.2.1"          :scope "test"
 26 |                   :exclusions [org.clojure/clojure
 27 |                                org.clojure/tools.nrepl
 28 |                                org.clojure/clojurescript]]
 29 |                  [pandeiro/boot-http          "0.8.3"          :scope "test"]
 30 |                  [adzerk/boot-cljs            "2.1.4"          :scope "test"
 31 |                   :exclusions [org.clojure/clojurescript]]
 32 |                  [adzerk/boot-cljs-repl       "0.3.3"          :scope "test"]
 33 |                  [adzerk/boot-test            "1.2.0"          :scope "test"]
 34 |                  [crisptrutski/boot-cljs-test "0.3.4"          :scope "test"
 35 |                   :exclusions [org.clojure/clojurescript]]
 36 |                  [adzerk/boot-reload          "0.5.2"          :scope "test"]
 37 |                  [org.clojure/tools.nrepl     "0.2.13"         :scope "test"]
 38 |                  [weasel                      "0.7.0"          :scope "test"]]
 39 |  :exclusions '[org.clojure/clojure org.clojure/clojurescript])
 40 | 
 41 | (require
 42 |  '[adzerk.boot-cljs            :refer [cljs]]
 43 |  '[adzerk.boot-cljs-repl       :as cr :refer [cljs-repl start-repl]]
 44 |  '[adzerk.boot-reload          :refer [reload]]
 45 |  '[adzerk.boot-test            :as bt]
 46 |  '[crisptrutski.boot-cljs-test :as bct]
 47 |  '[pandeiro.boot-http          :refer [serve]])
 48 | 
 49 | (deftask devcards []
 50 |   (set-env! :source-paths #(conj % "src/devcards")
 51 |             :resource-paths #{"resources/public"})
 52 |   (comp
 53 |     ;; remove possible artifacts of Figwheel compilation
 54 |     (sift :include #{#"^devcards\/(out\/|main.js)"} :invert true)
 55 |     (serve :port 3449)
 56 |     (watch)
 57 |     (cljs-repl)
 58 |     (reload)
 59 |     (speak)
 60 |     (cljs :source-map true
 61 |           :optimizations :none
 62 |           :compiler-options {:devcards true
 63 |                              :main 'om.devcards.core
 64 |                              :verbose true
 65 |                              :parallel-build true}
 66 |           :ids #{"devcards/main"})
 67 |     (target)))
 68 | 
 69 | (deftask testing []
 70 |   (set-env! :source-paths #(conj % "src/test"))
 71 |   identity)
 72 | 
 73 | (ns-unmap 'boot.user 'test)
 74 | 
 75 | (deftask test-clj []
 76 |   (comp
 77 |     (testing)
 78 |     (bt/test)))
 79 | 
 80 | (deftask test-cljs
 81 |   [e exit?     bool  "Enable flag."]
 82 |   (let [exit? (cond-> exit?
 83 |                 (nil? exit?) not)]
 84 |     (comp
 85 |       (testing)
 86 |       (bct/test-cljs
 87 |         :js-env :node
 88 |         :namespaces #{'om.next.tests}
 89 |         :cljs-opts {:parallel-build true
 90 |                     :target :nodejs
 91 |                     :asset-path "test_suite.out"}
 92 |         :exit? exit?
 93 |         :ids #{"lumo_test/test_suite"}))))
 94 | 
 95 | (deftask test
 96 |   [e exit?     bool  "Enable flag."]
 97 |   (let [exit? (cond-> exit?
 98 |                 (nil? exit?) not)]
 99 |     (comp
100 |       (test-clj)
101 |       (test-cljs :exit? exit?))))
102 | 
103 | (deftask auto-test []
104 |   (comp
105 |     (watch)
106 |     (speak)
107 |     (test :exit? false)))
108 | 


--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
 1 | machine:
 2 |   java:
 3 |     version: oraclejdk8
 4 | 
 5 | dependencies:
 6 |   pre:
 7 |     - wget https://github.com/boot-clj/boot-bin/releases/download/latest/boot.sh
 8 |     - mv boot.sh boot && chmod a+x boot && sudo mv boot /usr/local/bin
 9 |   override:
10 |     - boot > /dev/null
11 | 
12 | test:
13 |   override:
14 |     - boot test
15 | 


--------------------------------------------------------------------------------
/epl.html:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="ISO-8859-1" ?>
  2 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  3 | <html xmlns="http://www.w3.org/1999/xhtml">
  4 | 
  5 | <head>
  6 | <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
  7 | <title>Eclipse Public License - Version 1.0</title>
  8 | <style type="text/css">
  9 |   body {
 10 |     size: 8.5in 11.0in;
 11 |     margin: 0.25in 0.5in 0.25in 0.5in;
 12 |     tab-interval: 0.5in;
 13 |     }
 14 |   p {  	
 15 |     margin-left: auto;
 16 |     margin-top:  0.5em;
 17 |     margin-bottom: 0.5em;
 18 |     }
 19 |   p.list {
 20 |   	margin-left: 0.5in;
 21 |     margin-top:  0.05em;
 22 |     margin-bottom: 0.05em;
 23 |     }
 24 |   </style>
 25 | 
 26 | </head>
 27 | 
 28 | <body lang="EN-US">
 29 | 
 30 | <h2>Eclipse Public License - v 1.0</h2>
 31 | 
 32 | <p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
 33 | PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
 34 | DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
 35 | AGREEMENT.</p>
 36 | 
 37 | <p><b>1. DEFINITIONS</b></p>
 38 | 
 39 | <p>&quot;Contribution&quot; means:</p>
 40 | 
 41 | <p class="list">a) in the case of the initial Contributor, the initial
 42 | code and documentation distributed under this Agreement, and</p>
 43 | <p class="list">b) in the case of each subsequent Contributor:</p>
 44 | <p class="list">i) changes to the Program, and</p>
 45 | <p class="list">ii) additions to the Program;</p>
 46 | <p class="list">where such changes and/or additions to the Program
 47 | originate from and are distributed by that particular Contributor. A
 48 | Contribution 'originates' from a Contributor if it was added to the
 49 | Program by such Contributor itself or anyone acting on such
 50 | Contributor's behalf. Contributions do not include additions to the
 51 | Program which: (i) are separate modules of software distributed in
 52 | conjunction with the Program under their own license agreement, and (ii)
 53 | are not derivative works of the Program.</p>
 54 | 
 55 | <p>&quot;Contributor&quot; means any person or entity that distributes
 56 | the Program.</p>
 57 | 
 58 | <p>&quot;Licensed Patents&quot; mean patent claims licensable by a
 59 | Contributor which are necessarily infringed by the use or sale of its
 60 | Contribution alone or when combined with the Program.</p>
 61 | 
 62 | <p>&quot;Program&quot; means the Contributions distributed in accordance
 63 | with this Agreement.</p>
 64 | 
 65 | <p>&quot;Recipient&quot; means anyone who receives the Program under
 66 | this Agreement, including all Contributors.</p>
 67 | 
 68 | <p><b>2. GRANT OF RIGHTS</b></p>
 69 | 
 70 | <p class="list">a) Subject to the terms of this Agreement, each
 71 | Contributor hereby grants Recipient a non-exclusive, worldwide,
 72 | royalty-free copyright license to reproduce, prepare derivative works
 73 | of, publicly display, publicly perform, distribute and sublicense the
 74 | Contribution of such Contributor, if any, and such derivative works, in
 75 | source code and object code form.</p>
 76 | 
 77 | <p class="list">b) Subject to the terms of this Agreement, each
 78 | Contributor hereby grants Recipient a non-exclusive, worldwide,
 79 | royalty-free patent license under Licensed Patents to make, use, sell,
 80 | offer to sell, import and otherwise transfer the Contribution of such
 81 | Contributor, if any, in source code and object code form. This patent
 82 | license shall apply to the combination of the Contribution and the
 83 | Program if, at the time the Contribution is added by the Contributor,
 84 | such addition of the Contribution causes such combination to be covered
 85 | by the Licensed Patents. The patent license shall not apply to any other
 86 | combinations which include the Contribution. No hardware per se is
 87 | licensed hereunder.</p>
 88 | 
 89 | <p class="list">c) Recipient understands that although each Contributor
 90 | grants the licenses to its Contributions set forth herein, no assurances
 91 | are provided by any Contributor that the Program does not infringe the
 92 | patent or other intellectual property rights of any other entity. Each
 93 | Contributor disclaims any liability to Recipient for claims brought by
 94 | any other entity based on infringement of intellectual property rights
 95 | or otherwise. As a condition to exercising the rights and licenses
 96 | granted hereunder, each Recipient hereby assumes sole responsibility to
 97 | secure any other intellectual property rights needed, if any. For
 98 | example, if a third party patent license is required to allow Recipient
 99 | to distribute the Program, it is Recipient's responsibility to acquire
100 | that license before distributing the Program.</p>
101 | 
102 | <p class="list">d) Each Contributor represents that to its knowledge it
103 | has sufficient copyright rights in its Contribution, if any, to grant
104 | the copyright license set forth in this Agreement.</p>
105 | 
106 | <p><b>3. REQUIREMENTS</b></p>
107 | 
108 | <p>A Contributor may choose to distribute the Program in object code
109 | form under its own license agreement, provided that:</p>
110 | 
111 | <p class="list">a) it complies with the terms and conditions of this
112 | Agreement; and</p>
113 | 
114 | <p class="list">b) its license agreement:</p>
115 | 
116 | <p class="list">i) effectively disclaims on behalf of all Contributors
117 | all warranties and conditions, express and implied, including warranties
118 | or conditions of title and non-infringement, and implied warranties or
119 | conditions of merchantability and fitness for a particular purpose;</p>
120 | 
121 | <p class="list">ii) effectively excludes on behalf of all Contributors
122 | all liability for damages, including direct, indirect, special,
123 | incidental and consequential damages, such as lost profits;</p>
124 | 
125 | <p class="list">iii) states that any provisions which differ from this
126 | Agreement are offered by that Contributor alone and not by any other
127 | party; and</p>
128 | 
129 | <p class="list">iv) states that source code for the Program is available
130 | from such Contributor, and informs licensees how to obtain it in a
131 | reasonable manner on or through a medium customarily used for software
132 | exchange.</p>
133 | 
134 | <p>When the Program is made available in source code form:</p>
135 | 
136 | <p class="list">a) it must be made available under this Agreement; and</p>
137 | 
138 | <p class="list">b) a copy of this Agreement must be included with each
139 | copy of the Program.</p>
140 | 
141 | <p>Contributors may not remove or alter any copyright notices contained
142 | within the Program.</p>
143 | 
144 | <p>Each Contributor must identify itself as the originator of its
145 | Contribution, if any, in a manner that reasonably allows subsequent
146 | Recipients to identify the originator of the Contribution.</p>
147 | 
148 | <p><b>4. COMMERCIAL DISTRIBUTION</b></p>
149 | 
150 | <p>Commercial distributors of software may accept certain
151 | responsibilities with respect to end users, business partners and the
152 | like. While this license is intended to facilitate the commercial use of
153 | the Program, the Contributor who includes the Program in a commercial
154 | product offering should do so in a manner which does not create
155 | potential liability for other Contributors. Therefore, if a Contributor
156 | includes the Program in a commercial product offering, such Contributor
157 | (&quot;Commercial Contributor&quot;) hereby agrees to defend and
158 | indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
159 | against any losses, damages and costs (collectively &quot;Losses&quot;)
160 | arising from claims, lawsuits and other legal actions brought by a third
161 | party against the Indemnified Contributor to the extent caused by the
162 | acts or omissions of such Commercial Contributor in connection with its
163 | distribution of the Program in a commercial product offering. The
164 | obligations in this section do not apply to any claims or Losses
165 | relating to any actual or alleged intellectual property infringement. In
166 | order to qualify, an Indemnified Contributor must: a) promptly notify
167 | the Commercial Contributor in writing of such claim, and b) allow the
168 | Commercial Contributor to control, and cooperate with the Commercial
169 | Contributor in, the defense and any related settlement negotiations. The
170 | Indemnified Contributor may participate in any such claim at its own
171 | expense.</p>
172 | 
173 | <p>For example, a Contributor might include the Program in a commercial
174 | product offering, Product X. That Contributor is then a Commercial
175 | Contributor. If that Commercial Contributor then makes performance
176 | claims, or offers warranties related to Product X, those performance
177 | claims and warranties are such Commercial Contributor's responsibility
178 | alone. Under this section, the Commercial Contributor would have to
179 | defend claims against the other Contributors related to those
180 | performance claims and warranties, and if a court requires any other
181 | Contributor to pay any damages as a result, the Commercial Contributor
182 | must pay those damages.</p>
183 | 
184 | <p><b>5. NO WARRANTY</b></p>
185 | 
186 | <p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
187 | PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
188 | OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
189 | ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
190 | OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
191 | responsible for determining the appropriateness of using and
192 | distributing the Program and assumes all risks associated with its
193 | exercise of rights under this Agreement , including but not limited to
194 | the risks and costs of program errors, compliance with applicable laws,
195 | damage to or loss of data, programs or equipment, and unavailability or
196 | interruption of operations.</p>
197 | 
198 | <p><b>6. DISCLAIMER OF LIABILITY</b></p>
199 | 
200 | <p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
201 | NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
202 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
203 | WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
204 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
205 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
206 | DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
207 | HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
208 | 
209 | <p><b>7. GENERAL</b></p>
210 | 
211 | <p>If any provision of this Agreement is invalid or unenforceable under
212 | applicable law, it shall not affect the validity or enforceability of
213 | the remainder of the terms of this Agreement, and without further action
214 | by the parties hereto, such provision shall be reformed to the minimum
215 | extent necessary to make such provision valid and enforceable.</p>
216 | 
217 | <p>If Recipient institutes patent litigation against any entity
218 | (including a cross-claim or counterclaim in a lawsuit) alleging that the
219 | Program itself (excluding combinations of the Program with other
220 | software or hardware) infringes such Recipient's patent(s), then such
221 | Recipient's rights granted under Section 2(b) shall terminate as of the
222 | date such litigation is filed.</p>
223 | 
224 | <p>All Recipient's rights under this Agreement shall terminate if it
225 | fails to comply with any of the material terms or conditions of this
226 | Agreement and does not cure such failure in a reasonable period of time
227 | after becoming aware of such noncompliance. If all Recipient's rights
228 | under this Agreement terminate, Recipient agrees to cease use and
229 | distribution of the Program as soon as reasonably practicable. However,
230 | Recipient's obligations under this Agreement and any licenses granted by
231 | Recipient relating to the Program shall continue and survive.</p>
232 | 
233 | <p>Everyone is permitted to copy and distribute copies of this
234 | Agreement, but in order to avoid inconsistency the Agreement is
235 | copyrighted and may only be modified in the following manner. The
236 | Agreement Steward reserves the right to publish new versions (including
237 | revisions) of this Agreement from time to time. No one other than the
238 | Agreement Steward has the right to modify this Agreement. The Eclipse
239 | Foundation is the initial Agreement Steward. The Eclipse Foundation may
240 | assign the responsibility to serve as the Agreement Steward to a
241 | suitable separate entity. Each new version of the Agreement will be
242 | given a distinguishing version number. The Program (including
243 | Contributions) may always be distributed subject to the version of the
244 | Agreement under which it was received. In addition, after a new version
245 | of the Agreement is published, Contributor may elect to distribute the
246 | Program (including its Contributions) under the new version. Except as
247 | expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
248 | rights or licenses to the intellectual property of any Contributor under
249 | this Agreement, whether expressly, by implication, estoppel or
250 | otherwise. All rights in the Program not expressly granted under this
251 | Agreement are reserved.</p>
252 | 
253 | <p>This Agreement is governed by the laws of the State of New York and
254 | the intellectual property laws of the United States of America. No party
255 | to this Agreement will bring a legal action under this Agreement more
256 | than one year after the cause of action arose. Each party waives its
257 | rights to a jury trial in any resulting litigation.</p>
258 | 
259 | </body>
260 | 
261 | </html>
262 | 


--------------------------------------------------------------------------------
/examples/animation/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.animation.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/animation/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.animation.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state (atom {:count 0}))
 8 | 
 9 | (defn animation-view [app owner]
10 |   (reify
11 |     om/IWillMount
12 |     (will-mount [_]
13 |       (js/setInterval
14 |         (fn [] (om/transact! app :count inc))
15 |         16))
16 |     om/IRender
17 |     (render [_]
18 |       (dom/div nil (:count app)))))
19 | 
20 | (om/root animation-view app-state
21 |   {:target (.getElementById js/document "app")})
22 | 


--------------------------------------------------------------------------------
/examples/counters/index.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |     <body>
 3 |         <div id="app0"></div>
 4 |         <div id="app1"></div>
 5 |         <script src="out/goog/base.js" type="text/javascript"></script>
 6 |         <script src="main.js" type="text/javascript"></script>
 7 |         <script type="text/javascript">goog.require("examples.counters.core");</script>
 8 |     </body>
 9 | </html>
10 | 


--------------------------------------------------------------------------------
/examples/counters/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.counters.core
 2 |   (:require-macros [cljs.core.async.macros :refer [go]])
 3 |   (:require [om.core :as om :include-macros true]
 4 |             [om.dom :as dom :include-macros true]
 5 |             [cljs.core.async :refer [<! chan put! sliding-buffer]]))
 6 | 
 7 | (enable-console-print!)
 8 | 
 9 | (def app-state
10 |   (atom {:counters (into [] (map (fn [n] {:id n :count 0}) (range 10)))}))
11 | 
12 | (defn counter [data owner]
13 |   (reify
14 |     om/IRenderState
15 |     (render-state [_ {:keys [last-clicked]}]
16 |       (dom/div nil
17 |         (dom/label nil (:count data))
18 |         (dom/button
19 |           #js {:onClick
20 |                (fn [e]
21 |                  (om/transact! data :count inc)
22 |                  (put! last-clicked (.-path data)))}
23 |           "+")
24 |         (dom/button
25 |           #js {:onClick
26 |                (fn [e]
27 |                  (om/transact! data :count dec)
28 |                  (put! last-clicked (.-path data)))}
29 |           "-")))))
30 | 
31 | (defn counter-view [app owner]
32 |   (reify
33 |     om/IInitState
34 |     (init-state [_]
35 |       {:chans {:last-clicked (chan (sliding-buffer 1))}})
36 |     om/IWillMount
37 |     (will-mount [_]
38 |       (let [last-clicked (om/get-state owner [:chans :last-clicked])]
39 |         (go (while true
40 |               (let [lc (<! last-clicked)]
41 |                 (om/set-state! owner :message lc))))))
42 |     om/IRenderState
43 |     (render-state [_ {:keys [message chans]}]
44 |       (apply dom/div nil
45 |         (dom/h1 #js {:key "head"} "A Counting Widget!")
46 |         (dom/div
47 |           #js {:key "message"
48 |                :style 
49 |                (if message
50 |                  #js {:display "block"}
51 |                  #js {:display "none"})}
52 |           (when message
53 |             (str "Last clicked item was " (last message))))
54 |         (om/build-all counter (:counters app)
55 |           {:key :id :init-state chans})))))
56 | 
57 | (om/root counter-view app-state
58 |   {:target (.getElementById js/document "app0")
59 |    :descriptor (om/no-local-descriptor om/no-local-state-methods)
60 |    :tx-listen
61 |    (fn [tx-data root-cursor]
62 |      (println "listener 1: " tx-data))})
63 | 
64 | (om/root counter-view app-state
65 |   {:target (.getElementById js/document "app1")
66 |    :descriptor (om/no-local-descriptor om/no-local-state-methods)
67 |    :tx-listen
68 |    (fn [tx-data root-cursor]
69 |      (println "listener 2: " tx-data))})
70 | 
71 | 


--------------------------------------------------------------------------------
/examples/cursor_as_key/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.cursor_as_key.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/cursor_as_key/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.cursor-as-key.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state
 8 |   (atom
 9 |     {:selection [0 0]
10 |      :values
11 |      (into {}
12 |        (for [x (range 64)]
13 |          [[x x] (str "Hello " x)]))}))
14 | 
15 | (defn app [app-state owner]
16 |   (reify
17 |     om/IRender
18 |     (render [_]
19 |       (let [selection (app-state :selection)
20 |             values    (get-in app-state [:values selection])]
21 |         (dom/div nil values)))))
22 | 
23 | (om/root app app-state
24 |   {:target (.getElementById js/document "app")})
25 | 


--------------------------------------------------------------------------------
/examples/harmful/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.harmful.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/harmful/src/core.cljs:
--------------------------------------------------------------------------------
  1 | (ns examples.harmful.core
  2 |   (:require [om.core :as om :include-macros true]
  3 |             [om.dom :as dom :include-macros true])
  4 |   (:import [goog.ui IdGenerator]))
  5 | 
  6 | (enable-console-print!)
  7 | 
  8 | ;; =============================================================================
  9 | ;; Different backing React class
 10 | 
 11 | (defn get-gstate [owner]
 12 |   (aget (.-props owner) "__om_app_state"))
 13 | 
 14 | (defn merge-pending-state [owner]
 15 |   (let [gstate (get-gstate owner)
 16 |         spath  [:state-map (om/id owner)]
 17 |         states (get-in @gstate spath)]
 18 |     (when (:pending-state states)
 19 |       (swap! gstate update-in spath
 20 |         (fn [states]
 21 |           (-> states
 22 |             (assoc :render-state
 23 |               (merge (:render-state states) (:pending-state states)))
 24 |             (dissoc :pending-state)))))))
 25 | 
 26 | (def no-local-state-meths
 27 |   (assoc om/pure-methods
 28 |     :getInitialState
 29 |     (fn []
 30 |       (this-as this
 31 |         (let [c      (om/children this)
 32 |               props  (.-props this)
 33 |               istate (or (aget props "__om_init_state") {})
 34 |               id     (or (::om/id istate)
 35 |                          (.getNextUniqueId (.getInstance IdGenerator)))
 36 |               state  (merge (dissoc istate ::om/id)
 37 |                        (when (satisfies? om/IInitState c)
 38 |                          (om/allow-reads (om/init-state c))))
 39 |               spath  [:state-map id :render-state]]
 40 |           (aset props "__om_init_state" nil)
 41 |           (swap! (get-gstate this) assoc-in spath state)
 42 |           #js {:__om_id id})))
 43 |     :componentWillMount
 44 |     (fn []
 45 |       (this-as this
 46 |         (om/merge-props-state this)
 47 |         (let [c (om/children this)]
 48 |           (when (satisfies? om/IWillMount c)
 49 |             (om/allow-reads (om/will-mount c))))
 50 |         (merge-pending-state this)))
 51 |     :componentWillUnmount
 52 |     (fn []
 53 |       (this-as this
 54 |         (let [c     (om/children this)
 55 |               spath [:state-map (om/id this)]]
 56 |           (when (satisfies? om/IWillUnmount c)
 57 |             (om/allow-reads (om/will-unmount c)))
 58 |           (swap! (get-gstate this) update-in spath dissoc))))
 59 |     :componentDidUpdate
 60 |     (fn [prev-props prev-state]
 61 |       (this-as this
 62 |         (let [c      (om/children this)
 63 |               gstate (get-gstate this)
 64 |               states (get-in @gstate [:state-map (om/id this)])
 65 |               spath  [:state-map (om/id this)]]
 66 |           (when (satisfies? om/IDidUpdate c)
 67 |             (let [state (.-state this)]
 68 |               (om/allow-reads
 69 |                 (om/did-update c
 70 |                   (om/get-props #js {:props prev-props})
 71 |                   (or (:previous-state states)
 72 |                       (:render-state states))))))
 73 |           (when (:previous-state states)
 74 |             (swap! gstate update-in spath dissoc :previous-state)))))))
 75 | 
 76 | (def no-local-descriptor
 77 |   (specify! (clj->js no-local-state-meths)
 78 |     om/ISetState
 79 |     (-set-state!
 80 |       ([this val render]
 81 |          (om/allow-reads
 82 |            (let [props     (.-props this)
 83 |                  app-state (aget props "__om_app_state")
 84 |                  spath     [:state-map (om/id this) :pending-state]]
 85 |              (swap! (get-gstate this) assoc-in spath val)
 86 |              (when (and (not (nil? app-state)) render)
 87 |                (om/-queue-render! app-state this)))))
 88 |       ([this ks val render]
 89 |          (om/allow-reads
 90 |            (let [props     (.-props this)
 91 |                  app-state (aget props "__om_app_state")
 92 |                  spath     [:state-map (om/id this) :pending-state]]
 93 |              (swap! (get-gstate this) update-in spath assoc-in ks val)
 94 |              (when (and (not (nil? app-state)) render)
 95 |                (om/-queue-render! app-state this))))))
 96 |     om/IGetRenderState
 97 |     (-get-render-state
 98 |       ([this]
 99 |          (let [spath [:state-map (om/id this) :render-state]]
100 |            (get-in @(get-gstate this) spath)))
101 |       ([this ks]
102 |          (get-in (om/-get-render-state this) ks)))
103 |     om/IGetState
104 |     (-get-state
105 |       ([this]
106 |          (let [spath  [:state-map (om/id this)]
107 |                states (get-in @(get-gstate this) spath)]
108 |            (or (:pending-state states)
109 |              (:render-state states))))
110 |       ([this ks]
111 |          (get-in (om/-get-state this) ks)))))
112 | 
113 | ;; =============================================================================
114 | ;; Application
115 | 
116 | (def app-state (atom {:title "A Counter!"}))
117 | 
118 | (defn counter-view [data owner]
119 |   (reify
120 |     om/IInitState
121 |     (init-state [_]
122 |       {:count 0})
123 |     om/IRenderState 
124 |     (render-state [_ {:keys [count]}]
125 |       (dom/div nil
126 |         (dom/h2 nil (:title data))
127 |         (dom/div
128 |           #js {:onClick (fn [e] (om/set-state! owner :count (inc count)))}
129 |           count)))))
130 | 
131 | (defn label-style []
132 |   #js {:style #js {:width "60px" :display "inline-block"}})
133 | 
134 | (defn debug-view [[f cursor opts] owner]
135 |   (reify
136 |     om/IInitState
137 |     (init-state [_]
138 |       {:id (.getNextUniqueId (.getInstance IdGenerator))})
139 |     om/IDidMount
140 |     (did-mount [_]
141 |       (om/update-state! owner :id identity))
142 |     om/IRenderState
143 |     (render-state [_ {:keys [id]}]
144 |       (dom/div nil
145 |         (dom/div nil
146 |           (dom/label (label-style) "Props:")
147 |           (dom/code nil (pr-str (om/value cursor))))
148 |         (dom/div nil
149 |           (dom/label (label-style) "State:")
150 |           (dom/code nil
151 |             (pr-str (get-in @(om/state cursor) [:state-map id :render-state]))))
152 |         (om/build* f cursor
153 |           (assoc opts :init-state {::om/id id}))))))
154 | 
155 | (om/root
156 |   (fn [app owner]
157 |     (om/component
158 |       (om/build counter-view app {:descriptor no-local-descriptor})))
159 |   app-state
160 |   {:target (.getElementById js/document "app")
161 |    :instrument
162 |    (fn [f cursor opts]
163 |      (if (= f counter-view)
164 |        (om/build* debug-view [f cursor opts])
165 |        ::om/pass))})
166 | 
167 | 


--------------------------------------------------------------------------------
/examples/hello/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.hello.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/hello/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.hello.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state (atom {:foo "bar"}))
 8 | 
 9 | (defn widget [data owner]
10 |   (reify
11 |     om/ICheckState
12 |     om/IInitState
13 |     (init-state [_]
14 |       {:count 0})
15 |     om/IWillMount
16 |     (will-mount [_]
17 |       (println "Hello widget mounting"))
18 |     om/IWillUnmount
19 |     (will-unmount [_]
20 |       (println "Hello widget unmounting"))
21 |     om/IRenderState
22 |     (render-state [_ {:keys [count]}]
23 |       (println "Render!")
24 |       (dom/div nil
25 |         (dom/h2 nil "Hello world!")
26 |         (dom/p nil (str "Count: " count))
27 |         (dom/button
28 |           #js {:onClick
29 |                #(do
30 |                   (println "I can read!" (:foo data))
31 |                   (om/update-state! owner :count inc))}
32 |           "Bump!")
33 |         (dom/button
34 |           #js {:onClick
35 |                #(do
36 |                   (println "I can also read!" (:foo data))
37 |                   (om/update-state! owner :count identity))}
38 |           "Do Nothing")))))
39 | 
40 | (om/root widget app-state
41 |   {:target (.getElementById js/document "app")})
42 | 


--------------------------------------------------------------------------------
/examples/input/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.input.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/input/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.input.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (om/root
 8 |   (fn [app owner]
 9 |     (reify 
10 |       om/IInitState
11 |       (init-state [_]
12 |         {:value "" :count 0})
13 |       om/IRenderState
14 |       (render-state [_ {:keys [value]}]
15 |         (dom/div nil
16 |           (dom/label nil "Only numeric : ")
17 |           (dom/input #js
18 |             {:value value
19 |              :onChange
20 |              #(let [new-value (-> % .-target .-value)]
21 |                 (if (js/isNaN new-value)
22 |                   (om/set-state! owner :value value)
23 |                   (om/set-state! owner :value new-value)))})))))
24 |   {}
25 |   {:target (. js/document (getElementById "app"))})
26 | 
27 | 


--------------------------------------------------------------------------------
/examples/instrument/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.instrument.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/instrument/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.instrument.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (defn sub-view [item owner]
 8 |   (reify
 9 |     om/IRender
10 |     (render [_]
11 |       (dom/div nil (:text item)))))
12 | 
13 | (defn app-view [app owner]
14 |   (reify
15 |     om/IRender
16 |     (render [_]
17 |       (dom/div nil
18 |         (dom/div nil (:text app))
19 |         (apply dom/ul nil
20 |           (om/build-all sub-view (:list app)))))))
21 | 
22 | (defn something-else [original owner opts]
23 |   (reify
24 |     om/IRender
25 |     (render [_]
26 |       (dom/div #js {:style #js {:border "1px solid #ccc"
27 |                                 :padding "5px"}}
28 |         (dom/div nil
29 |           (dom/span nil "Path:")
30 |           (dom/pre #js {:style #js {:display "inline-block"}}
31 |             (pr-str (om/path (second original)))))
32 |         (apply om/build* original)))))
33 | 
34 | (om/root
35 |   app-view
36 |   (atom {:text "Instrument!"
37 |          :list [{:text "Milk"} {:text "Cookies"} {:text "Apples"}]})
38 |   {:target (.getElementById js/document "app")
39 |    :instrument
40 |    (fn [f cursor m]
41 |      (if (= f sub-view)
42 |        (om/build* something-else [f cursor m])
43 |        ::om/pass))})
44 | 


--------------------------------------------------------------------------------
/examples/mixins/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.mixins.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/mixins/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.mixins.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true])
 4 |   (:import [goog.ui IdGenerator]))
 5 | 
 6 | (enable-console-print!)
 7 | 
 8 | (def TestMixin
 9 |   #js {:componentWillMount
10 |        (fn []
11 |          (println "TextMixin componentWillMount"))})
12 | 
13 | (def MyComponent
14 |   (let [obj (om/specify-state-methods! (clj->js om/pure-methods))]
15 |     (aset obj "mixins" #js [TestMixin])
16 |     (js/React.createClass obj)))
17 | 
18 | (om/root
19 |   (fn [app owner]
20 |     (om/component
21 |       (MyComponent. nil
22 |         (dom/div nil "Hello world!"))))
23 |   {}
24 |   {:target (.getElementById js/document "app")})
25 | 


--------------------------------------------------------------------------------
/examples/mouse/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.mouse.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/mouse/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.mouse.core
 2 |   (:require-macros [cljs.core.async.macros :refer [go]])
 3 |   (:require [goog.events :as events]
 4 |             [cljs.core.async :as async :refer [>! <! put! chan]]
 5 |             [om.core :as om :include-macros true]
 6 |             [om.dom :as dom :include-macros true])
 7 |   (:import [goog.events EventType]))
 8 | 
 9 | (enable-console-print!)
10 | 
11 | (defn listen [el type]
12 |   (let [out (chan)]
13 |     (events/listen el type #(put! out %))
14 |     out))
15 | 
16 | (defn mouse-view [app owner]
17 |   (reify
18 |     om/IWillMount
19 |     (will-mount [_]
20 |       (let [mouse-chan
21 |             (async/map
22 |               (fn [e] [(.-clientX e) (.-clientY e)])
23 |               [(listen js/window EventType/MOUSEMOVE)])]
24 |         (go (while true
25 |               (om/update! app :mouse (<! mouse-chan))))))
26 |     om/IRender 
27 |     (render [_]
28 |       (dom/p nil
29 |         (when-let [pos (:mouse app)]
30 |           (pr-str (:mouse app)))))))
31 | 
32 | (om/root mouse-view {:mouse nil} {:target (.getElementById js/document "app")})
33 | 


--------------------------------------------------------------------------------
/examples/multi/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.multi.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/multi/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.multi.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state
 8 |   (atom
 9 |     {:widgets
10 |      [{:my-number 16}
11 |       {:my-number 23}]}))
12 | 
13 | (defmulti even-odd-widget
14 |   (fn [props _] (even? (:my-number props))))
15 | 
16 | (defmethod even-odd-widget true
17 |   [props owner]
18 |   (reify
19 |     om/IWillMount
20 |     (will-mount [_]
21 |       (println "Even widget mounting"))
22 |     om/IWillUnmount
23 |     (will-unmount [_]
24 |       (println "Even widget unmounting"))
25 |     om/IRender
26 |     (render [_]
27 |       (dom/div nil
28 |         (dom/h2 nil "Even Widget: " (:my-number props))
29 |         (dom/p nil (:text props))
30 |         (dom/button
31 |           #js {:onClick #(om/transact! props :my-number inc)}
32 |           "+")))))
33 | 
34 | (defmethod even-odd-widget false
35 |   [props owner]
36 |   (reify
37 |     om/IWillMount
38 |     (will-mount [_]
39 |       (println "Odd widget mounting"))
40 |     om/IWillUnmount
41 |     (will-unmount [_]
42 |       (println "Odd widget unmounting"))
43 |     om/IRender
44 |     (render [_]
45 |       (dom/div nil
46 |         (dom/h2 nil (str "Odd Widget: " (:my-number props)))
47 |         (dom/p nil (:text props))
48 |         (dom/button
49 |           #js {:onClick #(om/transact! props :my-number inc)}
50 |           "+")))))
51 | 
52 | (defn app [props owner]
53 |   (reify
54 |     om/IRender
55 |     (render [_]
56 |       (apply dom/div nil
57 |         (om/build-all even-odd-widget (:widgets props))))))
58 | 
59 | (om/root app app-state
60 |   {:target (.getElementById js/document "app")})
61 | 


--------------------------------------------------------------------------------
/examples/multiroot/index.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |     <body>
 3 |         <div id="app1"></div>
 4 |         <div id="app2"></div>
 5 |         <div id="modal1"></div>
 6 |         <div id="modal2"></div>
 7 |         <script src="out/goog/base.js" type="text/javascript"></script>
 8 |         <script src="main.js" type="text/javascript"></script>
 9 |         <script type="text/javascript">goog.require("examples.multiroot.core");</script>
10 |     </body>
11 | </html>
12 | 


--------------------------------------------------------------------------------
/examples/multiroot/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.multiroot.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state
 8 |   (atom {:text "Hello!"
 9 |          :modal1 {:text "Modal 1"}
10 |          :modal2 {:text "Modal 2"}}))
11 | 
12 | (defn display [data owner]
13 |   (om/component
14 |     (dom/div nil (:text data))))
15 | 
16 | (om/root display app-state {:target (.getElementById js/document "app1")})
17 | (om/root display app-state {:target (.getElementById js/document "app2")})
18 | 
19 | (swap! app-state assoc :text "Goodbye!")
20 | 
21 | (defn modal [data owner]
22 |   (om/component
23 |     (dom/div nil (:text data))))
24 | 
25 | (om/root modal app-state
26 |   {:target (.getElementById js/document "modal1")
27 |    :path [:modal1]})
28 | 
29 | (om/root modal app-state
30 |   {:target (.getElementById js/document "modal2")
31 |    :path [:modal2]})
32 | 


--------------------------------------------------------------------------------
/examples/refs/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.refs.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/refs/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.refs.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state
 8 |   (atom {:items [{:text "cat"}
 9 |                  {:text "dog"}
10 |                  {:text "bird"}]}))
11 | 
12 | (def app-history (atom [@app-state]))
13 | 
14 | (defn items []
15 |   (om/ref-cursor (:items (om/root-cursor app-state))))
16 | 
17 | (defn aview [{:keys [text]} owner]
18 |   (reify
19 |     om/IRender
20 |     (render [_]
21 |       (println "Render" text)
22 |       (let [xs (om/observe owner (items))]
23 |         (dom/div nil
24 |           (dom/h2 nil text)
25 |           (apply dom/ul nil
26 |             (map #(dom/li nil (:text %)) xs)))))))
27 | 
28 | (defn main-view [_ owner]
29 |   (reify
30 |     om/IRender
31 |     (render [_]
32 |       (println "Render Main View")
33 |       (let [xs (items)]
34 |         (dom/div nil
35 |           (om/build aview {:text "View A"})
36 |           (om/build aview {:text "View B"})
37 |           (dom/button
38 |             #js {:onClick
39 |                  (fn [e] (om/transact! xs #(assoc % 1 {:text "zebra"})))}
40 |             "Switch To Zebra!")
41 |           (dom/button
42 |             #js {:onClick
43 |                  (fn [e] (om/transact! xs #(assoc % 1 {:text "dog"})))}
44 |             "Switch to Dog!"))))))
45 | 
46 | (defn root [empty owner]
47 |   (reify
48 |     om/IRender
49 |     (render [_]
50 |       (println "Render Root")
51 |       (dom/div nil
52 |         (om/build main-view {})
53 |         (dom/div #js {:id "message"} nil)
54 |         (dom/button
55 |           #js {:onClick
56 |                (fn [e]
57 |                  (when (> (count @app-history) 1)
58 |                    (swap! app-history pop)
59 |                    (reset! app-state (last @app-history))))}
60 |           "Undo")))))
61 | 
62 | (om/root root app-state
63 |   {:target (.getElementById js/document "app")})
64 | 
65 | (defn pluralize [n s]
66 |   (if (== n 1)
67 |     s
68 |     (str s "s")))
69 | 
70 | (add-watch app-state :history
71 |   (fn [_ _ _ n]
72 |     (when-not (= (last @app-history) n)
73 |       (swap! app-history conj n))
74 |     (set! (.-innerHTML (.getElementById js/document "message"))
75 |       (let [c (count @app-history)]
76 |         (str c " Saved " (pluralize c "State"))))))
77 | 


--------------------------------------------------------------------------------
/examples/shared/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.shared.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/shared/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.shared.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state (atom {:title "Shared example"}))
 8 | 
 9 | (defn child-view [data owner]
10 |   (reify
11 |     om/IRender
12 |     (render [_]
13 |       (dom/div nil
14 |         (om/get-shared owner :some-text)))))
15 | 
16 | (defn shared-view [app owner]
17 |   (reify
18 |     om/IRender
19 |     (render [_]
20 |       (dom/div nil
21 |         (dom/h1 nil (:title app))
22 |         (dom/p nil
23 |           (om/get-shared owner :some-text))
24 |         (om/build child-view {})))))
25 | 
26 | (om/root shared-view app-state
27 |   {:target (.getElementById js/document "app")
28 |    :shared {:some-text "I'm shared!"}})
29 | 


--------------------------------------------------------------------------------
/examples/sortable/index.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |     <head>
 3 |         <style>
 4 |             #app {
 5 |                width: 300px;
 6 |             }
 7 |             .sortable {
 8 |                width: 300px;
 9 |             }
10 |             ul {
11 |                border-top: 1px solid #ccc;
12 |                border-left: 1px solid #ccc;
13 |                border-right: 1px solid #ccc;
14 |                margin: 0;
15 |                padding: 0;
16 |             }
17 |             li {
18 |                background-color: white;
19 |                padding: 4px;
20 |                list-style: none;
21 |                border-bottom: 1px solid #ccc;
22 |                user-select: none;
23 |                -moz-user-select: none;
24 |                -webkit-user-select: none;
25 |                cursor: move;
26 |                box-sizing: border-box;
27 |                -moz-box-sizing: border-box;
28 |                -webkit-box-sizing: border-box;
29 |             }
30 |             li.dragging {
31 |                border: 1px solid #ccc;
32 |             }
33 |             p {
34 |                padding: 0;
35 |                margin: 0;
36 |             }
37 |         </style>
38 |     </head>
39 |     <body>
40 |         <div id="app"></div>
41 |         <script src="out/goog/base.js" type="text/javascript"></script>
42 |         <script src="main.js" type="text/javascript"></script>
43 |         <script type="text/javascript">goog.require("examples.sortable.core");</script>
44 |     </body>
45 | </html>
46 | 


--------------------------------------------------------------------------------
/examples/sortable/src/core.cljs:
--------------------------------------------------------------------------------
  1 | (ns examples.sortable.core
  2 |   (:require-macros [cljs.core.async.macros :refer [go alt!]])
  3 |   (:require [cljs.core.async :as async :refer [put! chan dropping-buffer]]
  4 |             [om.core :as om :include-macros true]
  5 |             [om.dom :as dom :include-macros true]
  6 |             [goog.events :as events]
  7 |             [goog.style :as gstyle])
  8 |   (:import [goog.ui IdGenerator]
  9 |            [goog.events EventType]))
 10 | 
 11 | (enable-console-print!)
 12 | 
 13 | ;; =============================================================================
 14 | ;; Utilities
 15 | 
 16 | (defn guid []
 17 |   (.getNextUniqueId (.getInstance IdGenerator)))
 18 | 
 19 | (defn gsize->vec [size]
 20 |   [(.-width size) (.-height size)])
 21 | 
 22 | (defn to? [owner next-props next-state k]
 23 |   (or (and (not (om/get-render-state owner k))
 24 |            (k next-state))
 25 |       (and (not (k (om/get-props owner)))
 26 |            (k next-props))))
 27 | 
 28 | (defn from? [owner next-props next-state k]
 29 |   (or (and (om/get-render-state owner k)
 30 |            (not (k next-state)))
 31 |       (and (k (om/get-props owner))
 32 |            (not (k next-props)))))
 33 | 
 34 | (defn location [e]
 35 |   [(.-clientX e) (.-clientY e)])
 36 | 
 37 | (defn element-offset [el]
 38 |   (let [offset (gstyle/getPageOffset el)]
 39 |     [(.-x offset) (.-y offset)]))
 40 | 
 41 | ;; =============================================================================
 42 | ;; Generic Draggable
 43 | 
 44 | (defn dragging? [owner]
 45 |   (om/get-state owner :dragging))
 46 | 
 47 | (defn drag-start [e item owner]
 48 |   (when-not (dragging? owner)
 49 |     (let [el          (om/get-node owner "draggable")
 50 |           state       (om/get-state owner)
 51 |           drag-start  (location e)
 52 |           el-offset   (element-offset el)
 53 |           drag-offset (vec (map - el-offset drag-start))]
 54 |       ;; if in a sortable need to wait for sortable to
 55 |       ;; initiate dragging
 56 |       (when-not (:delegate state)
 57 |         (om/set-state! owner :dragging true))
 58 |       (doto owner
 59 |         (om/set-state! :location
 60 |           ((or (:constrain state) identity) el-offset))
 61 |         (om/set-state! :drag-offset drag-offset))
 62 |       (when-let [c (:chan state)]
 63 |         (put! c
 64 |           {:event :drag-start
 65 |            :id (:id item)
 66 |            :location (vec (map + drag-start drag-offset))})))))
 67 | 
 68 | (defn drag-stop [e item owner]
 69 |   (when (dragging? owner)
 70 |     (let [state (om/get-state owner)]
 71 |       (when (:dragging state)
 72 |         (om/set-state! owner :dragging false))
 73 |       ;; rendering order issues otherwise
 74 |       (when-not (:delegate state)
 75 |         (doto owner
 76 |           (om/set-state! :location nil)
 77 |           (om/set-state! :drag-offset nil)))
 78 |       (when-let [c (:chan state)]
 79 |         (put! c {:event :drag-stop :id (:id item)})))))
 80 | 
 81 | (defn drag [e item owner]
 82 |   (let [state (om/get-state owner)]
 83 |     (when (dragging? owner)
 84 |       (let [loc ((or (:constrain state) identity)
 85 |                   (vec (map + (location e) (:drag-offset state))))]
 86 |         (om/set-state! owner :location loc)
 87 |         (when-let [c (:chan state)]
 88 |           (put! c {:event :drag :location loc :id (:id item)}))))))
 89 | 
 90 | (defn draggable [item owner]
 91 |   (reify
 92 |     om/IDidMount
 93 |     (did-mount [_]
 94 |       ;; capture the cell dimensions when it becomes available
 95 |       (let [dims (-> (om/get-node owner "draggable")
 96 |                      gstyle/getSize gsize->vec)]
 97 |         (om/set-state! owner :dimensions dims)
 98 |         ;; let cell dimension listeners know
 99 |         (when-let [dims-chan (:dims-chan (om/get-state owner))]
100 |           (put! dims-chan dims))))
101 |     om/IWillUpdate
102 |     (will-update [_ next-props next-state]
103 |       ;; begin dragging, need to track events on window
104 |       (when (or (to? owner next-props next-state :dragging))
105 |         (let [mouse-up   #(drag-stop % @next-props owner)
106 |               mouse-move #(drag % @next-props owner)]
107 |           (om/set-state! owner :window-listeners
108 |             [mouse-up mouse-move])
109 |           (doto js/window
110 |             (events/listen EventType.MOUSEUP mouse-up)
111 |             (events/listen EventType.MOUSEMOVE mouse-move))))
112 |       ;; end dragging, cleanup window event listeners
113 |       (when (from? owner next-props next-state :dragging)
114 |         (let [[mouse-up mouse-move]
115 |               (om/get-state owner :window-listeners)]
116 |           (doto js/window
117 |             (events/unlisten EventType.MOUSEUP mouse-up)
118 |             (events/unlisten EventType.MOUSEMOVE mouse-move)))))
119 |     om/IRenderState
120 |     (render-state [_ state]
121 |       (let [style (cond
122 |                     (dragging? owner)
123 |                     (let [[x y] (:location state)
124 |                           [w h] (:dimensions state)]
125 |                       #js {:position "absolute"
126 |                            :top y :left x :z-index 1
127 |                            :width w :height h})
128 |                     :else
129 |                     #js {:position "static" :z-index 0})]
130 |         (dom/li
131 |           #js {:className (when (dragging? owner) "dragging")
132 |                :style style
133 |                :ref "draggable"
134 |                :onMouseDown #(drag-start % @item owner)
135 |                :onMouseUp   #(drag-stop % @item owner)
136 |                :onMouseMove #(drag % @item owner)}
137 |           (om/build (:view state) item))))))
138 | 
139 | ;; =============================================================================
140 | ;; Generic Sortable
141 | 
142 | (defn from-loc [v1 v2]
143 |   (vec (map - v2 v1)))
144 | 
145 | (defn sortable-spacer [height]
146 |   (dom/li
147 |     #js {:key "spacer-cell"
148 |          :style #js {:height height}}))
149 | 
150 | (defn index-of [x v]
151 |   (loop [i 0 v (seq v)]
152 |     (if v
153 |       (if (= x (first v))
154 |         i
155 |         (recur (inc i) (next v)))
156 |       -1)))
157 | 
158 | (defn insert-at [x idx ignore v]
159 |   (let [len (count v)]
160 |     (loop [i 0 v v ret []]
161 |       (if (>= i len)
162 |         (conj ret x)
163 |         (let [y (first v)]
164 |           (if (= y ignore)
165 |             (recur i (next v) (conj ret y))
166 |             (if (== i idx)
167 |               (into (conj ret x) v)
168 |               (recur (inc i) (next v) (conj ret y)))))))))
169 | 
170 | (defn sorting? [owner]
171 |   (om/get-state owner :sorting))
172 | 
173 | (defn start-sort [owner e]
174 |   (let [state (om/get-state owner)
175 |         sort  (:sort state)
176 |         idx   (index-of (:id e) sort)]
177 |     (doto owner
178 |       (om/set-state! :sorting (:id e))
179 |       (om/set-state! :real-sort sort)
180 |       (om/set-state! :drop-index idx)
181 |       (om/set-state! :sort (insert-at ::spacer idx (:id e) sort)))))
182 | 
183 | (defn handle-drop [owner e]
184 |   (when (sorting? owner)
185 |     (let [{:keys [sort drop-index]} (om/get-state owner)
186 |            idx (index-of ::spacer sort)
187 |            sort (->> sort
188 |                   (remove #{(:id e)})
189 |                   (replace {::spacer (:id e)})
190 |                   vec)]
191 |       (doto owner
192 |         (om/set-state! :sorting nil)
193 |         (om/set-state! :drop-index nil)
194 |         (om/set-state! :real-sort nil)
195 |         (om/set-state! :sort sort)))))
196 | 
197 | (defn update-drop [owner e]
198 |   (when (sorting? owner)
199 |     (let [loc    (:location e)
200 |           state  (om/get-state owner)
201 |           [_ y]  (from-loc (:location state) loc)
202 |           [_ ch] (:cell-dimensions state)
203 |           drop-index (js/Math.round (/ y ch))]
204 |       (when (not= (:drop-index state) drop-index)
205 |         (doto owner
206 |           (om/set-state! :drop-index drop-index)
207 |           (om/set-state! :sort
208 |             (insert-at ::spacer drop-index (:id e) (:real-sort state))))))))
209 | 
210 | (defn bound [n lb ub]
211 |   (cond
212 |     (< n lb) lb
213 |     (> n ub) ub
214 |     :else n))
215 | 
216 | (defn handle-drag-event [owner e]
217 |   (case (:event e)
218 |     :drag-start (start-sort owner e) 
219 |     :drag-stop  (handle-drop owner e)
220 |     :drag       (update-drop owner e)
221 |     nil))
222 | 
223 | (defn sortable [{:keys [items sort]} owner]
224 |   (reify
225 |     om/IInitState
226 |     (init-state [_] {:sort (om/value sort)})
227 |     om/IWillMount
228 |     (will-mount [_]
229 |       (let [drag-chan (chan)
230 |             dims-chan (chan (dropping-buffer 1))]
231 |         (om/set-state! owner :chans
232 |           {:drag-chan drag-chan
233 |            :dims-chan dims-chan})
234 |         (go
235 |           (while true
236 |             (alt!
237 |               drag-chan ([e c] (handle-drag-event owner e))
238 |               dims-chan ([e c] (om/set-state! owner :cell-dimensions e)))))))
239 |     ;; doesn't account for change in number of items in sortable
240 |     ;; nor changes in sort - exercise for the reader 
241 |     om/IWillUpdate
242 |     (will-update [_ next-props next-state]
243 |       ;; calculate constraints from cell-dimensions when we receive them
244 |       (when (to? owner next-props next-state :cell-dimensions)
245 |         (let [node   (om/get-node owner "sortable") 
246 |               [w h]  (gsize->vec (gstyle/getSize node))
247 |               [x y]  (element-offset node)
248 |               [_ ch] (:cell-dimensions next-state)]
249 |           (om/set-state! owner :constrain
250 |             (fn [[_ cy]] [(inc x) (bound cy y (- (+ y h) ch))])))))
251 |     om/IDidMount
252 |     (did-mount [_]
253 |       (when-not (om/get-state owner :location)
254 |         (om/set-state! owner :location
255 |           (element-offset (om/get-node owner "sortable")))))
256 |     om/IRenderState
257 |     (render-state [_ state]
258 |       (apply dom/ul #js {:className "sortable" :ref "sortable"}
259 |         (map
260 |           (fn [id]
261 |             (if-not (= id ::spacer)
262 |               (om/build draggable (items id)
263 |                 (let [{:keys [constrain chans view]} state]
264 |                   {:key :id
265 |                    :init-state {:view      view
266 |                                 :chan      (:drag-chan chans)
267 |                                 :dims-chan (:dims-chan chans)
268 |                                 :delegate  true}
269 |                    :state {:constrain constrain
270 |                            :dragging  (= id (:sorting state))}}))
271 |               (sortable-spacer (second (:cell-dimensions state)))))
272 |           (:sort state))))))
273 | 
274 | ;; =============================================================================
275 | ;; Example
276 | 
277 | (def app-state
278 |   (let [items (->> (take 10 (map vector (repeatedly guid) (range)))
279 |                 (map (fn [[id n]] [id {:id id :title n}]))
280 |                 (into {}))]
281 |     (atom {:items items
282 |            :sort (into [] (keys items))})))
283 | 
284 | (defn item [the-item owner]
285 |   (om/component (dom/span nil (str "Item " (:title the-item)))))
286 | 
287 | (defn sortable-view [app owner]
288 |   (om/component
289 |     (dom/div nil
290 |       (dom/h2 nil "Sortable example")
291 |       (om/build sortable app {:init-state {:view item}}))))
292 | 
293 | (om/root sortable-view app-state
294 |   {:target (.getElementById js/document "app")})
295 | 


--------------------------------------------------------------------------------
/examples/state_bug/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="main.js" type="text/javascript"></script>
5 |     </body>
6 | </html>
7 | 


--------------------------------------------------------------------------------
/examples/state_bug/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.state-bug.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state (atom {}))
 8 | 
 9 | (defn child-view [_ _ {:keys [inform!]}]
10 |   (reify
11 |     om/IRender
12 |     (render [_]
13 |       (inform!)
14 |       (dom/div nil "Child"))))
15 | 
16 | (defn parent-view [_ owner]
17 |   (reify
18 |     om/IRenderState
19 |     (render-state
20 |       [_ {:keys [foo] :as state}]
21 |       (dom/div nil
22 |         (str "Foo is: " (pr-str foo))
23 |         (dom/div nil
24 |           (om/build child-view
25 |             nil
26 |             {:opts {:inform! (fn [] (om/set-state! owner :foo "bar"))}}))))))
27 | 
28 | (defn application [data owner]
29 |   (reify
30 |     om/IInitState
31 |     (init-state [_]
32 |       {:cfg-init false})
33 |     om/IWillMount
34 |     (will-mount [_]
35 |       (js/setTimeout #(om/set-state! owner :cfg-init true) 500))
36 |     om/IRenderState
37 |     (render-state [_ {:keys [cfg-init]}]
38 |       ;; this works fine
39 |       #_(om/build parent-view {})
40 |       ;; start problem code
41 |       ;; comment out this code and uncomment the above to see it work
42 |       (if cfg-init
43 |         (om/build parent-view {})
44 |         (dom/div nil))
45 |       ;; end problem code
46 |       )))
47 | 
48 | (defn start [dom-id]
49 |   (om/root application app-state
50 |     {:target (.getElementById js/document dom-id)}))
51 | 
52 | (start "app")
53 | 


--------------------------------------------------------------------------------
/examples/stateful/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.stateful.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/stateful/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.stateful.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (defn counter-view [data owner]
 6 |   (reify
 7 |     om/IRender 
 8 |     (render [_]
 9 |       (dom/div nil
10 |         (dom/h2 nil "A Counter!")
11 |         (dom/div
12 |           #js {:onClick (fn [e] (om/transact! data :count inc))}
13 |           (:count data))))))
14 | 
15 | (defn applyf [x korks f]
16 |   (if (empty? korks)
17 |     (f x)
18 |     (update-in x korks f)))
19 | 
20 | (def MyClass*
21 |   (js/React.createClass
22 |     #js
23 |     {:getInitialState
24 |      (fn [] #js {:value {:count 0}})
25 |      :render
26 |      (fn []
27 |        (this-as this
28 |          (let [counter (aget (.-state this) "value")]
29 |            (om/build counter-view 
30 |              (specify! counter
31 |                IDeref
32 |                (-deref [this] this)
33 |                om/ITransact
34 |                (-transact! [m korks f _]
35 |                  (.setState this #js {:value (applyf m korks f)})))))))}))
36 | 
37 | (def MyClass (js/React.createFactory MyClass*))
38 | 
39 | (js/React.renderComponent (MyClass.) (.getElementById js/document "app"))
40 | 


--------------------------------------------------------------------------------
/examples/two_lists/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.two_lists.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/two_lists/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.two-lists.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (defprotocol IResolve
 8 |   (-resolve [this id]))
 9 | 
10 | (def app-state
11 |   (atom
12 |     {:list0 [:a :b :c]
13 |      :list1 [:d :e :f]
14 |      :sublist0 [:g :h :i]
15 |      :items
16 |      {:a {:text "cat"}
17 |       :b {:text "dog"}
18 |       :c {:text "bird"}
19 |       :d {:text "lion"}
20 |       :e {:text "antelope"}
21 |       :f {:text "zebra"}
22 |       :g {:text "anteater"}
23 |       :h {:text "hyena"}
24 |       :i {:text "tiger"}}}))
25 | 
26 | (defn removable [item id order]
27 |   (specify item
28 |     Object
29 |     (remove
30 |       ([this x]
31 |          (om/transact! order #(vec (remove #{id} %)))))))
32 | 
33 | (defn collection [order data]
34 |   (specify order
35 |     ISeqable
36 |     (-seq [_]
37 |       (map #(removable (get data %) % order) order))
38 |     ILookup
39 |     (-lookup
40 |       ([this k] (-lookup this k nil))
41 |       ([this k not-found]
42 |          (-nth this k not-found)))
43 |     IIndexed
44 |     (-nth
45 |       ([this k] (-nth this k nil))
46 |       ([this k not-found]
47 |          (let [id (nth order k)]
48 |            (removable (get data id) id order))))))
49 | 
50 | (defn item-view [aitem owner]
51 |   (reify
52 |     om/IRender
53 |     (render [_]
54 |       (dom/li nil
55 |         (dom/div nil (str (:text aitem)))
56 |         (dom/button
57 |           #js {:onClick #(.remove aitem)}
58 |           "Remove!")))))
59 | 
60 | (defn list-view [alist owner]
61 |   (reify
62 |     om/IRender
63 |     (render [_]
64 |       (apply dom/ul nil (om/build-all item-view alist)))))
65 | 
66 | (defn resolveable [x resolve-fn]
67 |   (if (om/cursor? x)
68 |     (specify x
69 |       ICloneable
70 |       (-clone [_]
71 |         (resolveable (-clone x) resolve-fn))
72 |       IResolve
73 |       (-resolve [this id]
74 |         (resolve-fn id this))
75 |       om/ICursorDerive
76 |       (-derive [this derived state path]
77 |         (resolveable (om/to-cursor derived state path) resolve-fn)))
78 |     x))
79 | 
80 | (defn resolve-id [id cursor]
81 |   (let [state (om/state cursor)]
82 |     (om/to-cursor (get @state id) [] state)))
83 | 
84 | (defn my-app [global owner]
85 |   (reify
86 |     om/IRender
87 |     (render [_]
88 |       (let [global (resolveable global resolve-id)
89 |             items  (:items global)]
90 |         (dom/div nil
91 |           (dom/h2 nil "Two Lists")
92 |           (om/build list-view (collection (:list0 global) items))
93 |           (om/build list-view (collection (:list1 global) items)))))))
94 | 
95 | (om/root my-app app-state
96 |   {:target (.getElementById js/document "app")})
97 | 


--------------------------------------------------------------------------------
/examples/typeahead/index.html:
--------------------------------------------------------------------------------
 1 | <html>
 2 |     <head>
 3 |         <style>
 4 |             ul {
 5 |                margin: 0;
 6 |                padding: 0;
 7 |             }
 8 |             li {
 9 |                list-style: none;
10 |             }
11 |             p {
12 |                padding: 0;
13 |                margin: 0;
14 |             }
15 |         </style>
16 |     </head>
17 |     <body>
18 |         <div id="app"></div>
19 |         <script src="out/goog/base.js" type="text/javascript"></script>
20 |         <script src="main.js" type="text/javascript"></script>
21 |         <script type="text/javascript">goog.require("examples.typeahead.core");</script>
22 |     </body>
23 | </html>
24 | 


--------------------------------------------------------------------------------
/examples/typeahead/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.typeahead.core
 2 |   (:refer-clojure :exclude [chars])
 3 |   (:require [om.core :as om :include-macros true]
 4 |             [om.dom :as dom :include-macros true]
 5 |             [clojure.string :as string]))
 6 | 
 7 | (enable-console-print!)
 8 | 
 9 | (def chars (into [] "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"))
10 | 
11 | (defn rand-char []
12 |   (nth chars (rand-int (count chars))))
13 | 
14 | (defn rand-word []
15 |   (apply str (take (inc (rand-int 10)) (repeatedly rand-char))))
16 | 
17 | (def app-state
18 |   (atom
19 |     {:words
20 |      (into []
21 |        (map (fn [w i] {:index i :word w :count (count w)})
22 |          (sort (into [] (take 200 (repeatedly rand-word))))
23 |          (range)))}))
24 | 
25 | (extend-type string
26 |   ICloneable
27 |   (-clone [s] (js/String. s)))
28 | 
29 | (extend-type js/String
30 |   om/IValue
31 |   (-value [s] (str s))
32 |   ICloneable
33 |   (-clone [s] (js/String. s)))
34 | 
35 | (extend-type number
36 |   ICloneable
37 |   (-clone [n] (js/Number. n)))
38 | 
39 | (extend-type js/Number
40 |   om/IValue
41 |   (-value [n] (.valueOf n))
42 |   ICloneable
43 |   (-clone [n] (js/Number. n)))
44 | 
45 | (defn hidden [^boolean bool]
46 |   (if bool
47 |     #js {:display "none"}
48 |     #js {:display "block"}))
49 | 
50 | ;; we have to use om/value because we've made
51 | ;; strings and numbers work as cursors and React doesn't
52 | ;; know how to handle these correctly
53 | 
54 | (defn word-index [index owner]
55 |   (om/component (dom/span nil (om/value index))))
56 | 
57 | (defn word-count [count owner]
58 |   (om/component (dom/span nil (om/value count))))
59 | 
60 | (defn word [the-word owner]
61 |   (om/component (dom/span nil (om/value the-word))))
62 | 
63 | (defn item [the-item owner]
64 |   (om/component
65 |     (dom/li #js {:style (hidden (:hidden the-item))}
66 |       (om/build word-index (:index the-item))
67 |       (dom/span nil " ")
68 |       (om/build word (:word the-item))
69 |       (dom/span nil " ")
70 |       (om/build word-count (:count the-item)))))
71 | 
72 | (defn change [e owner]
73 |   (om/set-state! owner :text (.. e -target -value)))
74 | 
75 | (defn typeahead [data owner]
76 |   (reify
77 |     om/IInitState
78 |     (init-state [_] {:text ""})
79 |     om/IRenderState
80 |     (render-state [_ {:keys [text]}]
81 |       (let [words (:words data)]
82 |         (dom/div nil
83 |           (dom/h2 nil "Type ahead example")
84 |           (dom/input
85 |             #js {:type "text"
86 |                  :ref "text-field"
87 |                  :value text
88 |                  :onChange #(change % owner)})
89 |           (apply dom/ul nil
90 |             (om/build-all item words
91 |               {:fn (fn [x]
92 |                      (if-not (string/blank? text)
93 |                        (cond-> x
94 |                          (not (zero? (.indexOf (:word x) text))) (assoc :hidden true))
95 |                        x))})))))))
96 | 
97 | (om/root typeahead app-state {:target (.getElementById js/document "app")})
98 | 


--------------------------------------------------------------------------------
/examples/unmount/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.unmount.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/unmount/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.unmount.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (defn widget-a [data owner]
 8 |   (reify
 9 |     om/IWillUnmount
10 |     (will-unmount [_]
11 |       (println "umounting Widget A"))
12 |     om/IRender
13 |     (render [_]
14 |       (dom/div nil "Widget A"))))
15 | 
16 | (defn widget-b [data owner]
17 |   (reify
18 |     om/IWillUnmount
19 |     (will-unmount [_]
20 |       (println "umounting Widget B"))
21 |     om/IRender
22 |     (render [_]
23 |       (dom/div nil "Widget B"))))
24 | 
25 | (defn app [data owner]
26 |   (reify
27 |     om/IRender
28 |     (render [_]
29 |       (dom/div nil
30 |         (if (= (:widget data) :a)
31 |           (om/build widget-a {})
32 |           (om/build widget-b {}))
33 |         (dom/button
34 |           #js {:onClick (fn [e] (om/transact! data :widget {:a :b :b :a}))}
35 |           "Switch!")))))
36 | 
37 | (om/root app {:widget :a}
38 |   {:target (.getElementById js/document "app")})
39 | 


--------------------------------------------------------------------------------
/examples/update_props/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.update_props.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/update_props/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.update-props.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (def app-state (atom {:widget {:count 0}}))
 8 | 
 9 | (defn widget [_ owner]
10 |   (reify
11 |     om/IRenderProps
12 |     (render-props [_ props _]
13 |       (println "Widget render!")
14 |       (dom/div nil
15 |         (dom/h2 nil "A Widget")
16 |         (dom/p nil (str "Count: " (:count props)))
17 |         (dom/button #js
18 |           {:onClick #(om/update-props! owner props [:count] inc)}
19 |           "+")))))
20 | 
21 | (defn my-app [global owner]
22 |   (reify
23 |     om/IRender
24 |     (render [_]
25 |       (println "Root render!")
26 |       (dom/div nil
27 |         (om/build widget (:widget global))))))
28 | 
29 | (om/root my-app app-state
30 |   {:target (.getElementById js/document "app")})
31 | 


--------------------------------------------------------------------------------
/examples/verify/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <div id="app"></div>
4 |         <script src="out/goog/base.js" type="text/javascript"></script>
5 |         <script src="main.js" type="text/javascript"></script>
6 |         <script type="text/javascript">goog.require("examples.verify.core");</script>
7 |     </body>
8 | </html>
9 | 


--------------------------------------------------------------------------------
/examples/verify/src/core.cljs:
--------------------------------------------------------------------------------
 1 | (ns examples.verify.core
 2 |   (:require [om.core :as om :include-macros true]
 3 |             [om.dom :as dom :include-macros true]))
 4 | 
 5 | (enable-console-print!)
 6 | 
 7 | (defn mincase [data owner]
 8 |   (reify
 9 |     om/IWillUpdate
10 |     (will-update [this next-props next-state]
11 |       (.log js/console "om/IWillUpdate invoked"))
12 |     om/IRender
13 |     (render [_]
14 |       (dom/div #js {:className "mincase"}
15 |         (when (:click-to-fail data) (dom/span nil "Clicked!"))
16 |         (dom/a
17 |           #js {:onClick #(om/update! data :click-to-fail :done)}
18 |           "Click me to trigger failure")))))
19 | 
20 | (om/root mincase
21 |   (atom {})
22 |   {:target (.getElementById js/document "app")})
23 | 


--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  2 |     <modelVersion>4.0.0</modelVersion>
  3 |     <groupId>org.omcljs</groupId>
  4 |     <artifactId>om</artifactId>
  5 |     <packaging>jar</packaging>
  6 |     <version>dev</version>
  7 |     <name>om</name>
  8 |     <description>A functional UI framework</description>
  9 |     <url>http://github.com/omcljs/om</url>
 10 |     <licenses>
 11 |         <license>
 12 |             <name>The Apache Software License, Version 2.0</name>
 13 |             <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
 14 |             <distribution>repo</distribution>
 15 |         </license>
 16 |     </licenses>
 17 |     <scm>
 18 |         <connection>scm:git:git://github.com/omcljs/om.git</connection>
 19 |         <developerConnection>scm:git:ssh://git@github.com/omcljs/om.git</developerConnection>
 20 |         <url>https://github.com/omcljs/om</url>
 21 |     </scm>
 22 |     <build>
 23 |         <sourceDirectory>src/main</sourceDirectory>
 24 |         <resources>
 25 |             <resource>
 26 |                 <directory>resources</directory>
 27 |             </resource>
 28 |             <resource>
 29 |                 <directory>src/devcards</directory>
 30 |             </resource>
 31 |         </resources>
 32 |         <testSourceDirectory>src/test</testSourceDirectory>
 33 |         <!--
 34 |         <directory>target</directory>
 35 |         <outputDirectory>target/classes</outputDirectory>
 36 |         -->
 37 |         <plugins>
 38 |         </plugins>
 39 |     </build>
 40 |     <repositories>
 41 |         <repository>
 42 |             <id>central</id>
 43 |             <url>http://repo1.maven.org/maven2/</url>
 44 |             <snapshots>
 45 |                 <enabled>false</enabled>
 46 |             </snapshots>
 47 |             <releases>
 48 |                 <enabled>true</enabled>
 49 |             </releases>
 50 |         </repository>
 51 |         <repository>
 52 |             <id>clojars</id>
 53 |             <url>https://clojars.org/repo/</url>
 54 |             <snapshots>
 55 |                 <enabled>true</enabled>
 56 |             </snapshots>
 57 |             <releases>
 58 |                 <enabled>true</enabled>
 59 |             </releases>
 60 |         </repository>
 61 |     </repositories>
 62 |     <dependencies>
 63 |         <dependency>
 64 |             <groupId>org.clojure</groupId>
 65 |             <artifactId>clojure</artifactId>
 66 |             <version>1.9.0</version>
 67 |             <scope>provided</scope>
 68 |         </dependency>
 69 |         <dependency>
 70 |             <groupId>org.clojure</groupId>
 71 |             <artifactId>clojurescript</artifactId>
 72 |             <version>1.9.908</version>
 73 |             <classifier>aot</classifier>
 74 |             <scope>provided</scope>
 75 |             <exclusions>
 76 |                 <exclusion>
 77 |                     <groupId>org.clojure</groupId>
 78 |                     <artifactId>data.json</artifactId>
 79 |                 </exclusion>
 80 |             </exclusions>
 81 |         </dependency>
 82 |         <dependency>
 83 |             <groupId>org.clojure</groupId>
 84 |             <artifactId>data.json</artifactId>
 85 |             <version>0.2.6</version>
 86 |             <classifier>aot</classifier>
 87 |             <scope>provided</scope>
 88 |         </dependency>
 89 |         <dependency>
 90 |             <groupId>org.clojure</groupId>
 91 |             <artifactId>core.async</artifactId>
 92 |             <version>0.3.443</version>
 93 |             <scope>test</scope>
 94 |             <exclusions>
 95 |                 <exclusion>
 96 |                     <groupId>org.clojure</groupId>
 97 |                     <artifactId>tools.reader</artifactId>
 98 |                 </exclusion>
 99 |             </exclusions>
100 |         </dependency>
101 |         <dependency>
102 |             <groupId>com.cognitect</groupId>
103 |             <artifactId>transit-clj</artifactId>
104 |             <version>0.8.300</version>
105 |         </dependency>
106 |         <dependency>
107 |             <groupId>com.cognitect</groupId>
108 |             <artifactId>transit-cljs</artifactId>
109 |             <version>0.8.239</version>
110 |         </dependency>
111 |         <dependency>
112 |             <groupId>cljsjs</groupId>
113 |             <artifactId>react</artifactId>
114 |             <version>16.0.0-0</version>
115 |         </dependency>
116 |         <dependency>
117 |             <groupId>cljsjs</groupId>
118 |             <artifactId>react-dom</artifactId>
119 |             <version>16.0.0-0</version>
120 |         </dependency>
121 |         <dependency>
122 |             <groupId>org.clojure</groupId>
123 |             <artifactId>tools.nrepl</artifactId>
124 |             <version>0.2.13</version>
125 |             <scope>test</scope>
126 |         </dependency>
127 |         <dependency>
128 |             <groupId>figwheel-sidecar</groupId>
129 |             <artifactId>figwheel-sidecar</artifactId>
130 |             <version>0.5.10</version>
131 |             <scope>test</scope>
132 |             <exclusions>
133 |                 <exclusion>
134 |                     <groupId>org.clojure</groupId>
135 |                     <artifactId>clojurescript</artifactId>
136 |                 </exclusion>
137 |                 <exclusion>
138 |                     <groupId>org.clojure</groupId>
139 |                     <artifactId>tools.reader</artifactId>
140 |                 </exclusion>
141 |             </exclusions>
142 |         </dependency>
143 |         <dependency>
144 |             <groupId>devcards</groupId>
145 |             <artifactId>devcards</artifactId>
146 |             <version>0.2.4</version>
147 |             <scope>test</scope>
148 |             <exclusions>
149 |                 <exclusion>
150 |                     <groupId>org.clojure</groupId>
151 |                     <artifactId>clojurescript</artifactId>
152 |                 </exclusion>
153 |             </exclusions>
154 |         </dependency>
155 |     </dependencies>
156 |     <developers>
157 |         <developer>
158 |             <name>David Nolen</name>
159 |             <email>david.nolen@gmail.com</email>
160 |         </developer>
161 |     </developers>
162 | </project>
163 | 


--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
  1 | (defproject org.omcljs/om "1.0.0-beta4"
  2 |   :description "ClojureScript interface to Facebook's React"
  3 |   :url "http://github.com/swannodette/om"
  4 |   :license {:name "Eclipse"
  5 |             :url "http://www.eclipse.org/legal/epl-v10.html"}
  6 | 
  7 |   :repositories [["clojars" {:sign-releases false}]]
  8 | 
  9 |   :jvm-opts ^:replace ["-Xms512m" "-Xmx512m" "-server"]
 10 | 
 11 |   :source-paths  ["src/main" "src/devcards" "src/test"]
 12 | 
 13 |   :dependencies [[org.clojure/clojure "1.9.0-alpha19" :scope "provided"]
 14 |                  [org.clojure/clojurescript "1.9.908" :scope "provided" :classifier "aot"
 15 |                   :exclusions [org.clojure/clojure
 16 |                                org.clojure/data.json]]
 17 |                  [org.clojure/data.json "0.2.6" :scope "provided" :classifier "aot"]
 18 |                  [cljsjs/react "16.0.0-0"]
 19 |                  [cljsjs/react-dom "16.0.0-0"]
 20 |                  [com.cognitect/transit-clj "0.8.300"]
 21 |                  [com.cognitect/transit-cljs "0.8.239"]
 22 | 
 23 |                  [org.clojure/core.async "0.2.385" :scope "test"
 24 |                   :exclusions [org.clojure/tools.reader]]
 25 |                  [figwheel-sidecar "0.5.10" :scope "test"
 26 |                   :exclusions [org.clojure/clojurescript
 27 |                                org.clojure/tools.reader]]
 28 |                  [devcards "0.2.4" :scope "test"
 29 |                   :exclusions [org.clojure/clojurescript]]]
 30 | 
 31 |   :plugins [[lein-cljsbuild "1.1.6"]]
 32 | 
 33 |   :jar-exclusions [#".DS_Store" #"dev" #"devcards" #"test" #"index.html"
 34 |                    #"main" #"public"]
 35 | 
 36 |   :clean-targets ^{:protect false} ["resources/out"]
 37 | 
 38 |   :cljsbuild {
 39 |     :builds [{:id "dev"
 40 |               :source-paths ["src/main" "src/dev"]
 41 |               :compiler {:main om.dev
 42 |                          :asset-path "out"
 43 |                          :output-to "resources/out/app.js"
 44 |                          :output-dir "resources/out"
 45 |                          :optimizations :none}}
 46 |              {:id "test"
 47 |               :source-paths ["src" "test"]
 48 |               :compiler {
 49 |                 :output-to "script/tests.simple.js"
 50 |                 :output-dir "script/out"
 51 |                 :source-map "script/tests.simple.js.map"
 52 |                 :output-wrapper false
 53 |                 :optimizations :simple}}
 54 |              ;; examples
 55 |              {:id "hello"
 56 |               :source-paths ["src" "examples/hello/src"]
 57 |               :compiler {
 58 |                 :output-to "examples/hello/main.js"
 59 |                 :output-dir "examples/hello/out"
 60 |                 :source-map true
 61 |                 :optimizations :none}}
 62 |              {:id "state-bug"
 63 |               :source-paths ["src" "examples/state_bug/src"]
 64 |               :compiler {
 65 |                 :main examples.state-bug.core
 66 |                 :asset-path "out"
 67 |                 :output-to "examples/state_bug/main.js"
 68 |                 :output-dir "examples/state_bug/out"
 69 |                 :source-map true
 70 |                 :optimizations :none}}
 71 |              {:id "verify"
 72 |               :source-paths ["src" "examples/verify/src"]
 73 |               :compiler {
 74 |                 :output-to "examples/verify/main.js"
 75 |                 :output-dir "examples/verify/out"
 76 |                 :source-map true
 77 |                 :optimizations :none}}
 78 |              {:id "input"
 79 |               :source-paths ["src" "examples/input/src"]
 80 |               :compiler {
 81 |                 :output-to "examples/input/main.js"
 82 |                 :output-dir "examples/input/out"
 83 |                 :source-map true
 84 |                 :optimizations :none}}
 85 |              {:id "multi"
 86 |               :source-paths ["src" "examples/multi/src"]
 87 |               :compiler {
 88 |                 :output-to "examples/multi/main.js"
 89 |                 :output-dir "examples/multi/out"
 90 |                 :source-map true
 91 |                 :optimizations :none}}
 92 |              {:id "cursor-as-key"
 93 |               :source-paths ["src" "examples/cursor_as_key/src"]
 94 |               :compiler {
 95 |                 :output-to "examples/cursor_as_key/main.js"
 96 |                 :output-dir "examples/cursor_as_key/out"
 97 |                 :source-map true
 98 |                 :optimizations :none}}
 99 |              {:id "unmount"
100 |               :source-paths ["src" "examples/unmount/src"]
101 |               :compiler {
102 |                 :output-to "examples/unmount/main.js"
103 |                 :output-dir "examples/unmount/out"
104 |                 :source-map true
105 |                 :optimizations :none}}
106 |              {:id "mouse"
107 |               :source-paths ["src" "examples/mouse/src"]
108 |               :compiler {
109 |                 :output-to "examples/mouse/main.js"
110 |                 :output-dir "examples/mouse/out"
111 |                 :source-map true
112 |                 :optimizations :none}}
113 |              {:id "multiroot"
114 |               :source-paths ["src" "examples/multiroot/src"]
115 |               :compiler {
116 |                 :output-to "examples/multiroot/main.js"
117 |                 :output-dir "examples/multiroot/out"
118 |                 :source-map true
119 |                 :optimizations :none}}
120 |              {:id "counters"
121 |               :source-paths ["src" "examples/counters/src"]
122 |               :compiler {
123 |                 :output-to "examples/counters/main.js"
124 |                 :output-dir "examples/counters/out"
125 |                 :source-map true
126 |                 :optimizations :none}}
127 |              {:id "animation"
128 |               :source-paths ["src" "examples/animation/src"]
129 |               :compiler {
130 |                 :output-to "examples/animation/main.js"
131 |                 :output-dir "examples/animation/out"
132 |                 :source-map true
133 |                 :optimizations :none}}
134 |              {:id "shared"
135 |               :source-paths ["src" "examples/shared/src"]
136 |               :compiler {
137 |                 :output-to "examples/shared/main.js"
138 |                 :output-dir "examples/shared/out"
139 |                 :source-map true
140 |                 :optimizations :none}}
141 |              {:id "typeahead"
142 |               :source-paths ["src" "examples/typeahead/src"]
143 |               :compiler {
144 |                 :output-to "examples/typeahead/main.js"
145 |                 :output-dir "examples/typeahead/out"
146 |                 :source-map true
147 |                 :optimizations :none}}
148 |              {:id "sortable"
149 |               :source-paths ["src" "examples/sortable/src"]
150 |               :compiler {
151 |                 :output-to "examples/sortable/main.js"
152 |                 :output-dir "examples/sortable/out"
153 |                 :source-map true
154 |                 :optimizations :none}}
155 |              {:id "instrument"
156 |               :source-paths ["src" "examples/instrument/src"]
157 |               :compiler {
158 |                 :output-to "examples/instrument/main.js"
159 |                 :output-dir "examples/instrument/out"
160 |                 :source-map true
161 |                 :optimizations :none}}
162 |              {:id "stateful"
163 |               :source-paths ["src" "examples/stateful/src"]
164 |               :compiler {
165 |                 :output-to "examples/stateful/main.js"
166 |                 :output-dir "examples/stateful/out"
167 |                 :source-map true
168 |                 :optimizations :none}}
169 |              {:id "harmful"
170 |               :source-paths ["src" "examples/harmful/src"]
171 |               :compiler {
172 |                 :output-to "examples/harmful/main.js"
173 |                 :output-dir "examples/harmful/out"
174 |                 :source-map true
175 |                 :optimizations :none}}
176 |              {:id "mixins"
177 |               :source-paths ["src" "examples/mixins/src"]
178 |               :compiler {
179 |                 :output-to "examples/mixins/main.js"
180 |                 :output-dir "examples/mixins/out"
181 |                 :source-map true
182 |                 :optimizations :none}}
183 |              {:id "two-lists"
184 |               :source-paths ["src" "examples/two_lists/src"]
185 |               :compiler {
186 |                 :output-to "examples/two_lists/main.js"
187 |                 :output-dir "examples/two_lists/out"
188 |                 :source-map true
189 |                 :optimizations :none}}
190 |              {:id "update-props"
191 |               :source-paths ["src" "examples/update_props/src"]
192 |               :compiler {
193 |                 :output-to "examples/update_props/main.js"
194 |                 :output-dir "examples/update_props/out"
195 |                 :source-map true
196 |                 :optimizations :none}}
197 |              {:id "refs"
198 |               :source-paths ["src" "examples/refs/src"]
199 |               :compiler {
200 |                 :output-to "examples/refs/main.js"
201 |                 :output-dir "examples/refs/out"
202 |                 :source-map true
203 |                 :optimizations :none}}
204 |              {:id "tests"
205 |               :source-paths ["src" "examples/tests/src"]
206 |               :compiler {
207 |                 :output-to "examples/tests/main.js"
208 |                 :output-dir "examples/tests/out"
209 |                 :source-map true
210 |                 :optimizations :none}}]})
211 | 


--------------------------------------------------------------------------------
/resources/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html>
 3 | <head lang="en">
 4 |     <meta charset="UTF-8">
 5 |     <title></title>
 6 | </head>
 7 | <body>
 8 |     <div id="app"></div>
 9 |     <script src="out/app.js"></script>
10 | </body>
11 | </html>


--------------------------------------------------------------------------------
/resources/public/devcards/index.html:
--------------------------------------------------------------------------------
 1 | <!DOCTYPE html>
 2 | <html>
 3 | <head lang="en">
 4 |     <meta charset="UTF-8">
 5 |     <title></title>
 6 | </head>
 7 | <body>
 8 |     <div id="app"></div>
 9 |     <script src="main.js"></script>
10 | </body>
11 | </html>


--------------------------------------------------------------------------------
/resources/public/devcards/main.cljs.edn:
--------------------------------------------------------------------------------
1 | {:require [om.devcards.core]
2 |  :compiler-options {:asset-path "main.out"}}
3 | 


--------------------------------------------------------------------------------
/script/build.clj:
--------------------------------------------------------------------------------
 1 | (require '[cljs.build.api :as b])
 2 | 
 3 | (b/build (b/inputs "src/main" "src/devcards")
 4 |   {:main 'om.devcards.core
 5 |    :asset-path "/devcards/out"
 6 |    :output-to "resources/public/devcards/main.js"
 7 |    :output-dir "resources/public/devcards/out"
 8 |    :parallel-build true
 9 |    :compiler-stats true
10 |    :verbose true})
11 | 
12 | (System/exit 0)
13 | 


--------------------------------------------------------------------------------
/script/figwheel.clj:
--------------------------------------------------------------------------------
 1 | (require '[figwheel-sidecar.repl :as r]
 2 |          '[figwheel-sidecar.repl-api :as ra])
 3 | 
 4 | (ra/start-figwheel!
 5 |   {:figwheel-options {}
 6 |    :build-ids ["devcards"]
 7 |    :all-builds
 8 |    [{:id "devcards"
 9 |      :figwheel {:devcards true}
10 |      :source-paths ["src/main" "src/devcards"]
11 |      :compiler {:main 'om.devcards.core
12 |                 :asset-path "/devcards/out"
13 |                 :output-to "resources/public/devcards/main.js"
14 |                 :output-dir "resources/public/devcards/out"
15 |                 :parallel-build true
16 |                 :compiler-stats true
17 |                 :verbose true}}]})
18 | 
19 | (ra/cljs-repl)
20 | 


--------------------------------------------------------------------------------
/script/index.html:
--------------------------------------------------------------------------------
1 | <html>
2 |     <body>
3 |         <script src="tests.simple.js"></script>
4 |     </body>
5 | </html>
6 | 


--------------------------------------------------------------------------------
/script/repl.clj:
--------------------------------------------------------------------------------
1 | (require '[cljs.repl :as repl])
2 | (require '[cljs.repl.node :as node])
3 | 
4 | (cljs.repl/repl* (node/repl-env)
5 |   {:verbose true
6 |    :parallel-build true
7 |    :compiler-stats true})
8 | 


--------------------------------------------------------------------------------
/script/test.clj:
--------------------------------------------------------------------------------
 1 | (require '[cljs.build.api :as b])
 2 | 
 3 | (b/build (b/inputs "src/main" "src/test")
 4 |   {:target :nodejs
 5 |    :main 'om.next.run-tests
 6 |    :output-to "target/test/test.js"
 7 |    :output-dir "target/test/out"
 8 |    :parallel-build true
 9 |    :compiler-stats true
10 |    :static-fns true
11 |    :verbose true})
12 | 
13 | (System/exit 0)
14 | 


--------------------------------------------------------------------------------
/script/watch.clj:
--------------------------------------------------------------------------------
 1 | (require '[cljs.build.api :as b])
 2 | 
 3 | (b/watch (b/inputs "src/main" "src/test/om/next")
 4 |   {:main 'om.next.tests
 5 |    :output-to "target/test/main.js"
 6 |    :output-dir "target/test/out"
 7 |    :parallel-build true
 8 |    :compiler-stats true
 9 |    :verbose true})
10 | 
11 | (System/exit 0)


--------------------------------------------------------------------------------
/src/devcards/om/devcards/autocomplete.cljs:
--------------------------------------------------------------------------------
  1 | (ns om.devcards.autocomplete
  2 |   (:require-macros [cljs.core.async.macros :refer [go]])
  3 |   (:require [cljs.core.async :as async :refer [<! >! put! chan]]
  4 |             [devcards.core :refer-macros [defcard deftest dom-node]]
  5 |             [clojure.string :as string]
  6 |             [om.next :as om :refer-macros [defui]]
  7 |             [om.dom :as dom])
  8 |   (:import [goog Uri]
  9 |            [goog.net Jsonp]))
 10 | 
 11 | (enable-console-print!)
 12 | 
 13 | (def base-url
 14 |   "http://en.wikipedia.org/w/api.php?action=opensearch&format=json&search=")
 15 | 
 16 | (defn jsonp
 17 |   ([uri] (jsonp (chan) uri))
 18 |   ([c uri]
 19 |    (let [gjsonp (Jsonp. (Uri. uri))]
 20 |      (.send gjsonp nil #(put! c %))
 21 |      c)))
 22 | 
 23 | ;; -----------------------------------------------------------------------------
 24 | ;; Parsing
 25 | 
 26 | (defmulti read om/dispatch)
 27 | 
 28 | (defmethod read :search/results
 29 |   [{:keys [state ast] :as env} k {:keys [query]}]
 30 |   (merge
 31 |     {:value (get @state k [])}
 32 |     (when-not (and (string/blank? query)
 33 |                    (<= 2 (count query)))
 34 |       {:search ast})))
 35 | 
 36 | ;; -----------------------------------------------------------------------------
 37 | ;; App
 38 | 
 39 | (defn result-list [results]
 40 |   (dom/ul #js {:key "result-list"}
 41 |     (map #(dom/li nil %) results)))
 42 | 
 43 | (defn search-field [ac query]
 44 |   (dom/input
 45 |     #js {:key "search-field"
 46 |          :value query
 47 |          :onChange
 48 |          (fn [e]
 49 |            (om/set-query! ac
 50 |              {:params {:query (.. e -target -value)}}))}))
 51 | 
 52 | (defui AutoCompleter
 53 |   static om/IQueryParams
 54 |   (params [_]
 55 |     {:query ""})
 56 |   static om/IQuery
 57 |   (query [_]
 58 |     '[(:search/results {:query ?query})])
 59 |   Object
 60 |   (render [this]
 61 |     (let [{:keys [search/results]} (om/props this)]
 62 |       (dom/div nil
 63 |         (dom/h2 nil "Autocompleter")
 64 |         (cond->
 65 |           [(search-field this (:query (om/get-params this)))]
 66 |           (not (empty? results)) (conj (result-list results)))))))
 67 | 
 68 | (defn search-loop [c]
 69 |   (go
 70 |     (loop [[query cb remote] (<! c)]
 71 |       (if-not (empty? query)
 72 |         (let [[_ results] (<! (jsonp (str base-url query)))]
 73 |           (cb {:search/results results} query remote))
 74 |         (cb {:search/results []} query remote))
 75 |       (recur (<! c)))))
 76 | 
 77 | (defn send-to-chan [c]
 78 |   (fn [{:keys [search]} cb]
 79 |     (when search
 80 |       (let [{[search] :children} (om/query->ast search)
 81 |             query (get-in search [:params :query])]
 82 |         (put! c [query cb :search])))))
 83 | 
 84 | (def send-chan (chan))
 85 | 
 86 | (def reconciler
 87 |   (om/reconciler
 88 |     {:state   {:search/results []}
 89 |      :parser  (om/parser {:read read})
 90 |      :send    (send-to-chan send-chan)
 91 |      :remotes [:remote :search]}))
 92 | 
 93 | (search-loop send-chan)
 94 | 
 95 | (defcard test-autocomplete
 96 |   "Demonstrate simple autocompleter"
 97 |   (dom-node
 98 |     (fn [_ node]
 99 |       (om/add-root! reconciler AutoCompleter node))))
100 | 


--------------------------------------------------------------------------------
/src/devcards/om/devcards/core.cljs:
--------------------------------------------------------------------------------
  1 | (ns om.devcards.core
  2 |   (:require [cljs.test :refer-macros [is async]]
  3 |             [devcards.core :refer-macros [defcard deftest dom-node start-devcard-ui!]]
  4 |             [cljs.pprint :as pprint]
  5 |             [om.devcards.utils :as utils]
  6 |             [om.devcards.tutorials]
  7 |             [om.devcards.bugs]
  8 |             [om.devcards.autocomplete]
  9 |             [om.devcards.shared-fn-test]
 10 |             [om.next :as om :refer-macros [defui]]
 11 |             [om.dom :as dom]))
 12 | 
 13 | (enable-console-print!)
 14 | (start-devcard-ui!)
 15 | 
 16 | (defui Hello
 17 |   Object
 18 |   (render [this]
 19 |     (dom/p nil (-> this om/props :text))))
 20 | 
 21 | (def hello (om/factory Hello))
 22 | 
 23 | (defcard simple-component
 24 |   "Test that Om Next component work as regular React components."
 25 |   (hello {:text "Hello, world!"}))
 26 | 
 27 | (def p
 28 |   (om/parser
 29 |     {:read   (fn [_ _ _] {:remote true})
 30 |      :mutate (fn [_ _ _] {:remote true})}))
 31 | 
 32 | (def r
 33 |   (om/reconciler
 34 |     {:parser p
 35 |      :ui->ref (fn [c] (-> c om/props :id))}))
 36 | 
 37 | (defui Binder
 38 |   Object
 39 |   (componentDidMount [this]
 40 |     (let [indexes @(get-in (-> this om/props :reconciler) [:config :indexer])]
 41 |       (om/update-state! this assoc :indexes indexes)))
 42 |   (render [this]
 43 |     (binding [om/*reconciler* (-> this om/props :reconciler)]
 44 |       (apply dom/div nil
 45 |         (hello {:id 0 :text "Goodbye, world!"})
 46 |         (when-let [indexes (get-in (om/get-state this)
 47 |                              [:indexes :ref->components])]
 48 |           [(dom/p nil (pr-str indexes))])))))
 49 | 
 50 | (def binder (om/factory Binder))
 51 | 
 52 | (defcard basic-nested-component
 53 |   "Test that component nesting works"
 54 |   (binder {:reconciler r}))
 55 | 
 56 | (deftest test-indexer
 57 |   "Test indexer"
 58 |   (let [idxr (get-in r [:config :indexer])]
 59 |     (is (not (nil? idxr)) "Indexer is not nil in the reconciler")
 60 |     (is (not (nil? @idxr)) "Indexer is IDeref")))
 61 | 
 62 | ;; -----------------------------------------------------------------------------
 63 | ;; Counters
 64 | 
 65 | (defmulti counters-read (fn [_ k _] k))
 66 | 
 67 | (defmulti counters-mutate (fn [_ k _] k))
 68 | 
 69 | (defmethod counters-read :default
 70 |   [{:keys [state]} k params]
 71 |   (let [st @state]
 72 |     (if (contains? st k)
 73 |       {:value (get st k)}
 74 |       {:remote true})))
 75 | 
 76 | (defmethod counters-read :counters/list
 77 |   [{:keys [state query]} _ _]
 78 |   (let [st @state
 79 |         xf (map #(select-keys (get-in st %) query))]
 80 |     {:value (into [] xf (:counters/list st))}))
 81 | 
 82 | (defmethod counters-mutate 'counter/increment
 83 |   [{:keys [state ref]} _ _]
 84 |   {:value {:keys []}
 85 |    :action
 86 |    (fn []
 87 |      (swap! state update-in (conj ref :counter/count) inc))})
 88 | 
 89 | (defmethod counters-mutate 'counters/delete
 90 |   [{:keys [state ref]} _ _]
 91 |   {:value {:keys [:counters/list]}
 92 |    :action
 93 |    (fn []
 94 |      (swap! state
 95 |        (fn [state]
 96 |          (-> state
 97 |            (update-in (pop ref) dissoc (peek ref))
 98 |            (update-in [:counters/list] #(vec (remove #{ref} %)))))))})
 99 | 
100 | (defmethod counters-mutate 'counters/create
101 |   [{:keys [state]} _ _]
102 |   {:value {:keys [:counters/list]}
103 |    :action
104 |    (fn []
105 |      (swap! state
106 |        (fn [state]
107 |          (let [id (:app/current-id state)
108 |                counter {:id id :counter/count 0}]
109 |            (-> state
110 |              (assoc-in [:app/counters id] counter)
111 |              (update-in [:counters/list] conj [:app/counters id])
112 |              (update-in [:app/current-id] inc))))))})
113 | 
114 | ;; -----------------------------------------------------------------------------
115 | ;; Counter
116 | 
117 | (defui Counter
118 |   static om/IQuery
119 |   (query [this]
120 |     '[:id :counter/count])
121 |   static om/Ident
122 |   (ident [this {:keys [id]}]
123 |     [:app/counters id])
124 |   Object
125 |   (initLocalState [this]
126 |     {:state-count 0})
127 |   (componentWillUnmount [this]
128 |     (println "Buh-bye!"))
129 |   (componentWillUpdate [this next-props next-state]
130 |     (println "component will update" (om/props this) next-props))
131 |   (componentDidUpdate [this prev-props prev-state]
132 |     #_(println "component did update" (om/props this) prev-props))
133 |   (render [this]
134 |     (println "Shared Counter" (om/shared this))
135 |     (let [{:keys [:counter/count] :as props} (om/props this)]
136 |       (dom/div nil
137 |         (dom/p nil
138 |           (str "Count: " count
139 |             " State Count: " (om/get-state this :state-count)))
140 |         (dom/button
141 |           #js {:onClick
142 |                (fn [_] (om/transact! this '[(counter/increment)]))}
143 |           "Update Props, Click Me!")
144 |         (dom/button
145 |           #js {:style #js {:marginLeft "10px"}
146 |                :onClick
147 |                (fn [_] (om/update-state! this update-in [:state-count] inc))}
148 |           "Update State, Click Me!")
149 |         (dom/button
150 |           #js {:style #js {:marginLeft "10px"}
151 |                :onClick
152 |                (fn [_] (om/transact! this '[(counters/delete) :counters/list]))}
153 |           "Delete")))))
154 | 
155 | (def counter (om/factory Counter {:keyfn :id}))
156 | 
157 | ;; -----------------------------------------------------------------------------
158 | ;; CountersAppTitle
159 | 
160 | (defui CountersAppTitle
161 |   Object
162 |   (render [this]
163 |     (apply dom/div nil
164 |       (om/children this))))
165 | 
166 | (def counters-app-title (om/factory CountersAppTitle))
167 | 
168 | ;; -----------------------------------------------------------------------------
169 | ;; CountersApp
170 | 
171 | (defui CountersApp
172 |   static om/IQueryParams
173 |   (params [this]
174 |     {:counter (om/get-query Counter)})
175 |   static om/IQuery
176 |   (query [this]
177 |     '[:app/title {:counters/list ?counter}])
178 |   Object
179 |   (render [this]
180 |     (println "Shared CountersApp" (om/shared this))
181 |     (let [{:keys [:app/title :counters/list] :as props}
182 |           (om/props this)]
183 |       (apply dom/div nil
184 |         (counters-app-title nil
185 |           (dom/h2 #js {:key "a"} "Hello World!")
186 |           (dom/h3 #js {:key "b"} "cool stuff"))
187 |         (dom/div nil
188 |           (dom/button
189 |             #js {:onClick (fn [e] (om/transact! this '[(counters/create)]))}
190 |             "Add Counter!"))
191 |         (map counter list)))))
192 | 
193 | ;; -----------------------------------------------------------------------------
194 | ;; Reconciler setup
195 | 
196 | (def counters-app-state
197 |   (atom {:app/title "Hello World!"
198 |          :app/current-id 3
199 |          :app/counters
200 |          {0 {:id 0 :counter/count 0}
201 |           1 {:id 1 :counter/count 0}
202 |           2 {:id 2 :counter/count 0}}
203 |          :counters/list [[:app/counters 0]
204 |                          [:app/counters 1]
205 |                          [:app/counters 2]]}))
206 | 
207 | (def counters-reconciler
208 |   (om/reconciler
209 |     {:state     counters-app-state
210 |      :parser    (om/parser {:read counters-read :mutate counters-mutate})
211 |      :shared-fn (fn [_] {:foo :bar})}))
212 | 
213 | (defcard test-counters
214 |   "Test that we can mock a reconciler backed Om Next component into devcards"
215 |   (dom-node
216 |     (fn [_ node]
217 |       (om/add-root! counters-reconciler CountersApp node))))
218 | 
219 | (defcard test-counters-atom
220 |   (om/app-state counters-reconciler))
221 | 
222 | ;; -----------------------------------------------------------------------------
223 | ;; Children
224 | 
225 | (defui Children
226 |   Object
227 |   (render [this]
228 |     (dom/div nil
229 |       (map identity
230 |         #js [(dom/div nil "Foo")
231 |              (dom/div nil "Bar")
232 |              (map identity
233 |                #js [(dom/div nil "Bar")
234 |                     (dom/div nil "Woz")])]))))
235 | 
236 | (def children (om/factory Children))
237 | 
238 | (defcard test-lazy-children
239 |   "Test that lazy sequences as elements works. This permits React.js style
240 |    splicing."
241 |   (children))
242 | 
243 | ;; -----------------------------------------------------------------------------
244 | ;; Simple Recursive Query Syntax
245 | 
246 | (def simple-tree-data
247 |   {:tree {:node-value 1
248 |           :children [{:node-value 2
249 |                       :children [{:node-value 3
250 |                                   :children []}]}
251 |                      {:node-value 4
252 |                       :children []}]}})
253 | 
254 | (declare simple-node)
255 | 
256 | (defui SimpleNode
257 |   static om/IQuery
258 |   (query [this]
259 |     '[:node-value {:children ...}])
260 |   Object
261 |   (render [this]
262 |     (let [{:keys [node-value children]} (om/props this)]
263 |       (dom/li nil
264 |         (dom/div nil (str "Node value:" node-value))
265 |         (dom/ul nil
266 |           (map simple-node children))))))
267 | 
268 | (def simple-node (om/factory SimpleNode))
269 | 
270 | (defui SimpleTree
271 |   static om/IQuery
272 |   (query [this]
273 |     [{:tree (om/get-query SimpleNode)}])
274 |   Object
275 |   (render [this]
276 |     (let [{:keys [tree]} (om/props this)]
277 |       (dom/ul nil
278 |         (simple-node tree)))))
279 | 
280 | (defmulti simple-tree-read om/dispatch)
281 | 
282 | (defmethod simple-tree-read :node-value
283 |   [{:keys [data] :as env} _ _]
284 |   {:value (:node-value data)})
285 | 
286 | (defmethod simple-tree-read :children
287 |   [{:keys [data parser query] :as env} _ _]
288 |   {:value (let [f #(parser (assoc env :data %) query)]
289 |             (into [] (map f (:children data))))})
290 | 
291 | (defmethod simple-tree-read :tree
292 |   [{:keys [state parser query] :as env} k _]
293 |   (let [st @state]
294 |     {:value (parser (assoc env :data (:tree st)) query)}))
295 | 
296 | (def simple-tree-reconciler
297 |   (om/reconciler
298 |     {:state     (atom simple-tree-data)
299 |      :normalize false
300 |      :parser    (om/parser {:read simple-tree-read})}))
301 | 
302 | (defcard test-simple-recursive-syntax
303 |   "Test that `'[:node-value {:children ...}]` syntax works."
304 |   (dom-node
305 |     (fn [_ node]
306 |       (om/add-root! simple-tree-reconciler SimpleTree node))))
307 | 
308 | ;; -----------------------------------------------------------------------------
309 | ;; Recursive Query Syntax with Mutations
310 | 
311 | (def norm-tree-data
312 |   {:tree {:id 0
313 |           :node-value 1
314 |           :children [{:id 1
315 |                       :node-value 2
316 |                       :children [{:id 2
317 |                                   :node-value 3
318 |                                   :children []}]}
319 |                      {:id 3
320 |                       :node-value 4
321 |                       :children []}]}})
322 | 
323 | (declare norm-node)
324 | 
325 | (defmulti norm-tree-read om/dispatch)
326 | 
327 | (defmethod norm-tree-read :tree
328 |   [{:keys [state query] :as env} _ _]
329 |   (let [st @state]
330 |     {:value (om/db->tree query (:tree st) st)}))
331 | 
332 | (defmethod norm-tree-read :node/by-id
333 |   [{:keys [state query query-root]} _ _]
334 |   {:value (om/db->tree query query-root @state)})
335 | 
336 | (defmulti norm-tree-mutate om/dispatch)
337 | 
338 | (defmethod norm-tree-mutate 'tree/increment
339 |   [{:keys [state]} _ {:keys [id]}]
340 |   {:action
341 |    (fn []
342 |      (swap! state update-in [:node/by-id id :node-value] inc))})
343 | 
344 | (defmethod norm-tree-mutate 'tree/decrement
345 |   [{:keys [state]} _ {:keys [id]}]
346 |   {:action
347 |    (fn []
348 |      (swap! state update-in [:node/by-id id :node-value]
349 |        (fn [n] (max 0 (dec n)))))})
350 | 
351 | (defn increment! [c id]
352 |   (fn [e]
353 |     (om/transact! c `[(tree/increment {:id ~id})])))
354 | 
355 | (defn decrement! [c id]
356 |   (fn [e]
357 |     (om/transact! c `[(tree/decrement {:id ~id})])))
358 | 
359 | (defui NormNode
360 |   static om/Ident
361 |   (ident [this {:keys [id]}]
362 |     [:node/by-id id])
363 |   static om/IQuery
364 |   (query [this]
365 |     '[:id :node-value {:children ...}])
366 |   Object
367 |   (render [this]
368 |     (let [{:keys [id node-value children]} (om/props this)]
369 |       (dom/li nil
370 |         (dom/div nil
371 |           (dom/label nil (str "Node value:" node-value))
372 |           (dom/button #js {:onClick (increment! this id)} "+")
373 |           (dom/button #js {:onClick (decrement! this id)} "-"))
374 |         (dom/ul nil
375 |           (map norm-node children))))))
376 | 
377 | (def norm-node (om/factory NormNode))
378 | 
379 | (defui NormTree
380 |   static om/IQuery
381 |   (query [this]
382 |     [{:tree (om/get-query NormNode)}])
383 |   Object
384 |   (render [this]
385 |     (let [{:keys [tree]} (om/props this)]
386 |       (dom/ul nil
387 |         (norm-node tree)))))
388 | 
389 | (def norm-tree-parser
390 |   (om/parser {:read   norm-tree-read
391 |               :mutate norm-tree-mutate}))
392 | 
393 | (def norm-tree-reconciler
394 |   (om/reconciler
395 |     {:state   norm-tree-data
396 |      :parser  norm-tree-parser
397 |      :pathopt true}))
398 | 
399 | (defcard test-simple-recursive-syntax-with-mutation
400 |   "Test that simple recursive syntax works with mutations and component
401 |    local state. Cool"
402 |   (dom-node
403 |     (fn [_ node]
404 |       (om/add-root! norm-tree-reconciler NormTree node))))
405 | 
406 | (comment
407 |   (pprint/pprint @(-> norm-tree-reconciler :config :indexer))
408 |   )
409 | 
410 | ;; -----------------------------------------------------------------------------
411 | ;; Instrumenting
412 | 
413 | (defui Wrapper
414 |   Object
415 |   (render [this]
416 |     (let [{:keys [factory props]} (om/props this)]
417 |       (dom/div nil
418 |        (dom/h2 nil "Wrapped")
419 |        (factory props)))))
420 | 
421 | (def wrapper (om/factory Wrapper))
422 | 
423 | (defui AView
424 |   Object
425 |   (render [this]
426 |     (dom/div nil "I'm a view!")))
427 | 
428 | (def aview (om/factory AView))
429 | 
430 | (defui RootView
431 |   Object
432 |   (render [this]
433 |     (aview {})))
434 | 
435 | (def instrument-reconciler
436 |   (om/reconciler
437 |     {:state {}
438 |      :parser (fn [_ _ _] {:value :not-found})
439 |      :instrument (fn [{:keys [props _ class factory]}]
440 |                    (println "instrument!" (= AView class)
441 |                      AView class)
442 |                    (if (= AView class)
443 |                      (wrapper {:factory (om/factory AView {:instrument? false}) :props props})
444 |                      (factory props)))}))
445 | 
446 | (defcard test-instrument
447 |   "Test that instrument interception works"
448 |   (dom-node
449 |     (fn [_ node]
450 |       (om/add-root! instrument-reconciler RootView node))))
451 | 


--------------------------------------------------------------------------------
/src/devcards/om/devcards/shared_fn_test.cljs:
--------------------------------------------------------------------------------
 1 | (ns om.devcards.shared-fn-test
 2 |   (:require [devcards.core :refer-macros [defcard deftest dom-node]]
 3 |             [om.next :as om :refer-macros [defui]]
 4 |             [om.dom :as dom]))
 5 | 
 6 | (defui Home
 7 |   static om/IQuery
 8 |   (query [this] [:counter])
 9 | 
10 |   Object
11 |   (render [this]
12 |     (let [shared (om/shared this)
13 |           props  (om/props this)]
14 |       (println "Render Root!")
15 |       (dom/div nil
16 |         (dom/h3 nil (str "Props: " props))
17 |         (dom/h3 nil (str "Shared: " shared))
18 |         (dom/button
19 |           #js {:onClick #(om/transact! this '[(my/test) :counter])}
20 |           "Increment!")))))
21 | 
22 | (def app-state (atom {:counter 0}))
23 | 
24 | (defn read
25 |   [env key params]
26 |   (let [{:keys [state]} env]
27 |     {:value (get @state key)}))
28 | 
29 | (defn mutate
30 |   [env key params]
31 |   (let [{:keys [state]} env]
32 |     {:value  {:keys [:counter]}
33 |      :action #(swap! state update-in [:counter] inc)}))
34 | 
35 | (def reconciler
36 |   (om/reconciler
37 |     {:state     app-state
38 |      :parser    (om/parser {:read read :mutate mutate})
39 |      :shared    {}
40 |      :shared-fn (fn [root-props]
41 |                   root-props)}))
42 | 
43 | (defcard test-om-478
44 |   "Test that re-running shared-fn works"
45 |   (dom-node
46 |     (fn [_ node]
47 |       (om/add-root! reconciler Home node))))
48 | 


--------------------------------------------------------------------------------
/src/devcards/om/devcards/tutorials.cljs:
--------------------------------------------------------------------------------
  1 | (ns om.devcards.tutorials
  2 |   (:require [cljs.test :refer-macros [is async]]
  3 |             [devcards.core :refer-macros [defcard deftest dom-node]]
  4 |             [om.next :as om :refer-macros [defui]]
  5 |             [om.dom :as dom]))
  6 | 
  7 | ;; =============================================================================
  8 | ;; Quick Start
  9 | 
 10 | (def animals-app-state
 11 |   (atom
 12 |     {:app/title "Animals"
 13 |      :animals/list
 14 |      [[1 "Ant"] [2 "Antelope"] [3 "Bird"] [4 "Cat"] [5 "Dog"]
 15 |       [6 "Lion"] [7 "Mouse"] [8 "Monkey"] [9 "Snake"] [10 "Zebra"]]}))
 16 | 
 17 | (defmulti animals-read (fn [env key params] key))
 18 | 
 19 | (defmethod animals-read :default
 20 |   [{:keys [state] :as env} key params]
 21 |   (let [st @state]
 22 |     (if-let [[_ value] (find st key)]
 23 |       {:value value}
 24 |       {:value :not-found})))
 25 | 
 26 | (defmethod animals-read :animals/list
 27 |   [{:keys [state] :as env} key {:keys [start end]}]
 28 |   {:value (subvec (:animals/list @state) start end)})
 29 | 
 30 | (defui AnimalsList
 31 |   static om/IQueryParams
 32 |   (params [this]
 33 |     {:start 0 :end 10})
 34 |   static om/IQuery
 35 |   (query [this]
 36 |     '[:app/title (:animals/list {:start ?start :end ?end})])
 37 |   Object
 38 |   (render [this]
 39 |     (let [{:keys [app/title animals/list]} (om/props this)]
 40 |       (dom/div nil
 41 |         (dom/h2 nil title)
 42 |         (apply dom/ul nil
 43 |           (map
 44 |             (fn [[i name]]
 45 |               (dom/li nil (str i ". " name)))
 46 |             list))))))
 47 | 
 48 | (def animals-reconciler
 49 |   (om/reconciler
 50 |     {:state animals-app-state
 51 |      :parser (om/parser {:read animals-read})}))
 52 | 
 53 | (defcard animals
 54 |   (dom-node
 55 |     (fn [_ node]
 56 |       (om/add-root! animals-reconciler AnimalsList node))))
 57 | 
 58 | ;; =============================================================================
 59 | ;; Componentes, Identity & Normalization
 60 | 
 61 | (enable-console-print!)
 62 | 
 63 | (def cian-init-data
 64 |   {:list/one [{:name "John" :points 0}
 65 |               {:name "Mary" :points 0}
 66 |               {:name "Bob"  :points 0}]
 67 |    :list/two [{:name "Mary" :points 0 :age 27}
 68 |               {:name "Gwen" :points 0}
 69 |               {:name "Jeff" :points 0}]})
 70 | 
 71 | ;; -----------------------------------------------------------------------------
 72 | ;; Parsing
 73 | 
 74 | (defmulti cian-read om/dispatch)
 75 | 
 76 | (defn get-people [state key]
 77 |   (let [st @state]
 78 |     (into [] (map #(get-in st %)) (get st key))))
 79 | 
 80 | (defmethod cian-read :list/one
 81 |   [{:keys [state] :as env} key params]
 82 |   {:value (get-people state key)})
 83 | 
 84 | (defmethod cian-read :list/two
 85 |   [{:keys [state] :as env} key params]
 86 |   {:value (get-people state key)})
 87 | 
 88 | (defmulti cian-mutate om/dispatch)
 89 | 
 90 | (defmethod cian-mutate 'points/increment
 91 |   [{:keys [state]} _ {:keys [name]}]
 92 |   {:action
 93 |    (fn []
 94 |      (swap! state update-in
 95 |        [:person/by-name name :points]
 96 |        inc))})
 97 | 
 98 | (defmethod cian-mutate 'points/decrement
 99 |   [{:keys [state]} _ {:keys [name]}]
100 |   {:action
101 |    (fn []
102 |      (swap! state update-in
103 |        [:person/by-name name :points]
104 |        #(let [n (dec %)] (if (neg? n) 0 n))))})
105 | 
106 | ;; -----------------------------------------------------------------------------
107 | ;; Components
108 | 
109 | (defui Person
110 |   static om/Ident
111 |   (ident [this {:keys [name]}]
112 |     [:person/by-name name])
113 |   static om/IQuery
114 |   (query [this]
115 |     '[:name :points :age])
116 |   Object
117 |   (render [this]
118 |     (println "Render Person" (-> this om/props :name))
119 |     (let [{:keys [points name foo] :as props} (om/props this)]
120 |       (dom/li nil
121 |         (dom/label nil (str name ", points: " points))
122 |         (dom/button
123 |           #js {:onClick
124 |                (fn [e]
125 |                  (om/transact! this
126 |                    `[(points/increment ~props)]))}
127 |           "+")
128 |         (dom/button
129 |           #js {:onClick
130 |                (fn [e]
131 |                  (om/transact! this
132 |                    `[(points/decrement ~props)]))}
133 |           "-")))))
134 | 
135 | (def person (om/factory Person {:keyfn :name}))
136 | 
137 | (defui ListView
138 |   Object
139 |   (render [this]
140 |     (println "Render ListView" (-> this om/path first))
141 |     (let [list (om/props this)]
142 |       (apply dom/ul nil
143 |         (map person list)))))
144 | 
145 | (def list-view (om/factory ListView))
146 | 
147 | (defui RootView
148 |   static om/IQuery
149 |   (query [this]
150 |     (let [subquery (om/get-query Person)]
151 |       `[{:list/one ~subquery} {:list/two ~subquery}]))
152 |   Object
153 |   (render [this]
154 |     (println "Render RootView")
155 |     (let [{:keys [list/one list/two]} (om/props this)]
156 |       (apply dom/div nil
157 |         [(dom/h2 nil "List A")
158 |          (list-view one)
159 |          (dom/h2 nil "List B")
160 |          (list-view two)]))))
161 | 
162 | (def cian-reconciler
163 |   (om/reconciler
164 |     {:state  cian-init-data
165 |      :parser (om/parser {:read cian-read :mutate cian-mutate})}))
166 | 
167 | (defcard component-identity-and-normalization
168 |   (dom-node
169 |     (fn [_ node]
170 |       (om/add-root! cian-reconciler RootView node))))
171 | 
172 | ;; =============================================================================
173 | ;; Thinking With Links!
174 | 
175 | (def links-init-data
176 |   {:current-user {:email "bob.smith@gmail.com"}
177 |    :items [{:id 0 :title "Foo"}
178 |            {:id 1 :title "Bar"}
179 |            {:id 2 :title "Baz"}]})
180 | 
181 | (defmulti links-read om/dispatch)
182 | 
183 | (defmethod links-read :items
184 |   [{:keys [query state]} k _]
185 |   (let [st @state]
186 |     {:value (om/db->tree query (get st k) st)}))
187 | 
188 | (defui LinksItem
189 |   static om/Ident
190 |   (ident [_ {:keys [id]}]
191 |     [:item/by-id id])
192 |   static om/IQuery
193 |   (query [_]
194 |     '[:id :title [:current-user _]])
195 |   Object
196 |   (render [this]
197 |     (let [{:keys [title current-user]} (om/props this)]
198 |       (dom/li nil
199 |         (dom/div nil title)
200 |         (dom/div nil (:email current-user))))))
201 | 
202 | (def links-item (om/factory LinksItem))
203 | 
204 | (defui LinksSomeList
205 |   static om/IQuery
206 |   (query [_]
207 |     [{:items (om/get-query LinksItem)}])
208 |   Object
209 |   (render [this]
210 |     (dom/div nil
211 |       (dom/h2 nil "A List!")
212 |       (dom/ul nil
213 |         (map links-item (-> this om/props :items))))))
214 | 
215 | (def links-reconciler
216 |   (om/reconciler
217 |     {:state links-init-data
218 |      :parser (om/parser {:read links-read})}))
219 | 
220 | (defcard links
221 |   (dom-node
222 |     (fn [_ node]
223 |       (om/add-root! links-reconciler LinksSomeList node))))
224 | 
225 | ;; =============================================================================
226 | ;; Queries with unions
227 | 
228 | (def union-init-data
229 |   {:dashboard/items
230 |    [{:id 0 :type :dashboard/post
231 |      :author "Laura Smith"
232 |      :title "A Post!"
233 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
234 |      :favorites 0}
235 |     {:id 1 :type :dashboard/photo
236 |      :title "A Photo!"
237 |      :image "photo.jpg"
238 |      :caption "Lorem ipsum"
239 |      :favorites 0}
240 |     {:id 2 :type :dashboard/post
241 |      :author "Jim Jacobs"
242 |      :title "Another Post!"
243 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
244 |      :favorites 0}
245 |     {:id 3 :type :dashboard/graphic
246 |      :title "Charts and Stufff!"
247 |      :image "chart.jpg"
248 |      :favorites 0}
249 |     {:id 4 :type :dashboard/post
250 |      :author "May Fields"
251 |      :title "Yet Another Post!"
252 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
253 |      :favorites 0}]})
254 | 
255 | (defui Post
256 |   static om/IQuery
257 |   (query [this]
258 |     [:id :type :title :author :content])
259 |   Object
260 |   (render [this]
261 |     (let [{:keys [title author content] :as props} (om/props this)]
262 |       (dom/div nil
263 |         (dom/h3 nil title)
264 |         (dom/h4 nil author)
265 |         (dom/p nil content)))))
266 | 
267 | (def post (om/factory Post))
268 | 
269 | (defui Photo
270 |   static om/IQuery
271 |   (query [this]
272 |     [:id :type :title :image :caption])
273 |   Object
274 |   (render [this]
275 |     (let [{:keys [title image caption]} (om/props this)]
276 |       (dom/div nil
277 |         (dom/h3 nil (str "Photo: " title))
278 |         (dom/div nil image)
279 |         (dom/p nil "Caption: ")))))
280 | 
281 | (def photo (om/factory Photo))
282 | 
283 | (defui Graphic
284 |   static om/IQuery
285 |   (query [this]
286 |     [:id :type :title :image])
287 |   Object
288 |   (render [this]
289 |     (let [{:keys [title image]} (om/props this)]
290 |       (dom/div nil
291 |         (dom/h3 nil (str "Graphic: " title))
292 |         (dom/div nil image)))))
293 | 
294 | (def graphic (om/factory Graphic))
295 | 
296 | (defui DashboardItem
297 |   static om/Ident
298 |   (ident [this {:keys [id type]}]
299 |     [type id])
300 |   static om/IQuery
301 |   (query [this]
302 |     (zipmap
303 |       [:dashboard/post :dashboard/photo :dashboard/graphic]
304 |       (map #(conj % :favorites)
305 |         [(om/get-query Post)
306 |          (om/get-query Photo)
307 |          (om/get-query Graphic)])))
308 |   Object
309 |   (render [this]
310 |     (let [{:keys [id type favorites] :as props} (om/props this)]
311 |       (dom/li
312 |         #js {:style #js {:padding 10 :borderBottom "1px solid black"}}
313 |         (dom/div nil
314 |           (({:dashboard/post    post
315 |              :dashboard/photo   photo
316 |              :dashboard/graphic graphic} type)
317 |             (om/props this)))
318 |         (dom/div nil
319 |           (dom/p nil (str "Favorites: " favorites))
320 |           (dom/button
321 |             #js {:onClick
322 |                  (fn [e]
323 |                    (om/transact! this
324 |                      `[(dashboard/favorite {:ref [~type ~id]})]))}
325 |             "Favorite!"))))))
326 | 
327 | (def dashboard-item (om/factory DashboardItem))
328 | 
329 | (defui Dashboard
330 |   static om/IQuery
331 |   (query [this]
332 |     [{:dashboard/items (om/get-query DashboardItem)}])
333 |   Object
334 |   (render [this]
335 |     (let [{:keys [dashboard/items]} (om/props this)]
336 |       (apply dom/ul
337 |         #js {:style #js {:padding 0}}
338 |         (map dashboard-item items)))))
339 | 
340 | (defmulti union-read om/dispatch)
341 | 
342 | (defmethod union-read :dashboard/items
343 |   [{:keys [state]} k _]
344 |   (let [st @state]
345 |     {:value (into [] (map #(get-in st %)) (get st k))}))
346 | 
347 | (defmulti mutate om/dispatch)
348 | 
349 | (defmethod mutate 'dashboard/favorite
350 |   [{:keys [state]} k {:keys [ref]}]
351 |   {:action
352 |    (fn []
353 |      (swap! state update-in (conj ref :favorites) inc))})
354 | 
355 | (def union-reconciler
356 |   (om/reconciler
357 |     {:state  union-init-data
358 |      :parser (om/parser {:read union-read :mutate mutate})}))
359 | 
360 | (defcard union
361 |   (dom-node
362 |     (fn [_ node]
363 |       (om/add-root! union-reconciler Dashboard node))))
364 | 


--------------------------------------------------------------------------------
/src/devcards/om/devcards/utils.cljs:
--------------------------------------------------------------------------------
1 | (ns om.devcards.utils
2 |   (:require-macros [cljs.core.async.macros :refer [go]])
3 |   (:require [cljs.core.async :as async :refer [<! >! put! chan]]))
4 | 
5 | 


--------------------------------------------------------------------------------
/src/main/data_readers.clj:
--------------------------------------------------------------------------------
1 | {js clojure.core/identity}
2 | 


--------------------------------------------------------------------------------
/src/main/deps.cljs:
--------------------------------------------------------------------------------
1 | {:externs ["om/externs.js"]}
2 | 


--------------------------------------------------------------------------------
/src/main/om/checksums.clj:
--------------------------------------------------------------------------------
 1 | (ns om.checksums
 2 |   (:require [clojure.string :as str]))
 3 | 
 4 | ;; ===================================================================
 5 | ;; Checksums (data-react-checksum)
 6 | 
 7 | (def MOD 65521)
 8 | 
 9 | ;; Adapted from https://github.com/tonsky/rum
10 | (defn adler32 [^StringBuilder sb]
11 |   (let [l (.length sb)
12 |         m (bit-and l -4)]
13 |     (loop [a (int 1)
14 |            b (int 0)
15 |            i 0
16 |            n (min (+ i 4096) m)]
17 |       (cond
18 |         (< i n)
19 |         (let [c0 (int (.charAt sb i))
20 |               c1 (int (.charAt sb (+ i 1)))
21 |               c2 (int (.charAt sb (+ i 2)))
22 |               c3 (int (.charAt sb (+ i 3)))
23 |               b  (+ b a c0
24 |                    a c0 c1
25 |                    a c0 c1 c2
26 |                    a c0 c1 c2 c3)
27 |               a  (+ a c0 c1 c2 c3)]
28 |           (recur (rem a MOD) (rem b MOD) (+ i 4) n))
29 | 
30 |         (< i m)
31 |         (recur a b i (min (+ i 4096) m))
32 | 
33 |         (< i l)
34 |         (let [c0 (int (.charAt sb i))]
35 |           (recur (+ a c0) (+ b a c0) (+ i 1) n))
36 | 
37 |         :else
38 |         (let [a (rem a MOD)
39 |               b (rem b MOD)]
40 |           (bit-or (int a) (unchecked-int (bit-shift-left b 16))))))))
41 | 
42 | (defn assign-react-checksum [^StringBuilder sb]
43 |   (.insert sb (.indexOf sb ">") (str " data-react-checksum=\"" (adler32 sb) "\"")))
44 | 


--------------------------------------------------------------------------------
/src/main/om/core.clj:
--------------------------------------------------------------------------------
 1 | (ns om.core)
 2 | 
 3 | (defmacro component
 4 |   "Sugar over reify for quickly putting together components that
 5 |    only need to implement om.core/IRender and don't need access to
 6 |    the owner argument."
 7 |   [& body]
 8 |   `(reify
 9 |      om.core/IRender
10 |      (~'render [this#]
11 |        ~@body)))
12 | 
13 | 


--------------------------------------------------------------------------------
/src/main/om/dom.cljs:
--------------------------------------------------------------------------------
 1 | (ns om.dom
 2 |   (:refer-clojure :exclude [map mask meta time select])
 3 |   (:require-macros [om.dom :as dom])
 4 |   (:require [cljsjs.react]
 5 |             [cljsjs.react.dom]
 6 |             [om.util :as util]
 7 |             [goog.object :as gobj]))
 8 | 
 9 | (dom/gen-react-dom-fns)
10 | 
11 | (defn- update-state
12 |   "Updates the state of the wrapped input element."
13 |   [component next-props value]
14 |   (let [on-change (gobj/getValueByKeys component "state" "onChange")
15 |         next-state #js {}]
16 |     (gobj/extend next-state next-props #js {:onChange on-change})
17 |     (gobj/set next-state "value" value)
18 |     (.setState component next-state)))
19 | 
20 | (defn wrap-form-element [element]
21 |   (let [ctor (fn [props]
22 |                (this-as this
23 |                  (set! (.-state this)
24 |                    (let [state #js {}]
25 |                      (->> #js {:onChange (goog/bind (gobj/get this "onChange") this)}
26 |                        (gobj/extend state props))
27 |                      state))
28 |                  (.apply js/React.Component this (js-arguments))))]
29 |     (set! (.-displayName ctor) (str "wrapped-" element))
30 |     (goog.inherits ctor js/React.Component)
31 |     (specify! (.-prototype ctor)
32 |       Object
33 |       (onChange [this event]
34 |         (when-let [handler (.-onChange (.-props this))]
35 |           (handler event)
36 |           (update-state
37 |             this (.-props this)
38 |             (gobj/getValueByKeys event "target" "value"))))
39 | 
40 |       (componentWillReceiveProps [this new-props]
41 |         (let [state-value (gobj/getValueByKeys this "state" "value")
42 |               element-value (gobj/get (js/ReactDOM.findDOMNode this) "value")]
43 |           ;; On IE, onChange event might come after actual value of
44 |           ;; an element have changed. We detect this and render
45 |           ;; element as-is, hoping that next onChange will
46 |           ;; eventually come and bring our modifications anyways.
47 |           ;; Ignoring this causes skipped letters in controlled
48 |           ;; components
49 |           ;; https://github.com/facebook/react/issues/7027
50 |           ;; https://github.com/reagent-project/reagent/issues/253
51 |           ;; https://github.com/tonsky/rum/issues/86
52 |           ;; TODO: Find a better solution, since this conflicts
53 |           ;; with controlled/uncontrolled inputs.
54 |           ;; https://github.com/r0man/sablono/issues/148
55 |           (if (not= state-value element-value)
56 |             (update-state this new-props element-value)
57 |             (update-state this new-props (gobj/get new-props "value")))))
58 | 
59 |       (render [this]
60 |         (js/React.createElement element (.-state this))))
61 |     (js/React.createFactory ctor)))
62 | 
63 | (def input (wrap-form-element "input"))
64 | 
65 | (def textarea (wrap-form-element "textarea"))
66 | 
67 | (def option (wrap-form-element "option"))
68 | 
69 | (def select (wrap-form-element "select"))
70 | 
71 | (defn render
72 |   "Equivalent to React.render"
73 |   [component el]
74 |   (js/ReactDOM.render component el))
75 | 
76 | (defn render-to-str
77 |   "Equivalent to React.renderToString"
78 |   [c]
79 |   (js/ReactDOMServer.renderToString c))
80 | 
81 | (defn node
82 |   "Returns the dom node associated with a component's React ref."
83 |   ([component]
84 |    (js/ReactDOM.findDOMNode component))
85 |   ([component name]
86 |    (some-> (.-refs component) (gobj/get name) (js/ReactDOM.findDOMNode))))
87 | 
88 | (defn create-element
89 |   "Create a DOM element for which there exists no corresponding function.
90 |    Useful to create DOM elements not included in React.DOM. Equivalent
91 |    to calling `js/React.createElement`"
92 |   ([tag]
93 |    (create-element tag nil))
94 |   ([tag opts]
95 |    (js/React.createElement tag opts))
96 |   ([tag opts & children]
97 |    (js/React.createElement tag opts children)))
98 | 


--------------------------------------------------------------------------------
/src/main/om/externs.js:
--------------------------------------------------------------------------------
1 | var _reactInternalFiber;
2 | var stateNode;
3 | 


--------------------------------------------------------------------------------
/src/main/om/impl.cljs:
--------------------------------------------------------------------------------
1 | (ns om.impl)
2 | 
3 | (defn get-props
4 |   [x]
5 |   (aget (.-props x) "__om_cursor"))
6 | 


--------------------------------------------------------------------------------
/src/main/om/next/cache.cljs:
--------------------------------------------------------------------------------
 1 | (ns om.next.cache)
 2 | 
 3 | (deftype Cache [arr index size]
 4 |   Object
 5 |   (add [this id x]
 6 |     (let [x' (vary-meta x assoc :client-time (js/Date.))]
 7 |       (if (<= size (alength arr))
 8 |         (let [id' (.shift arr)]
 9 |           (swap! index #(-> % (dissoc id') (assoc id x'))))
10 |         (swap! index assoc id x')))
11 |     (.push arr id))
12 |   (get [this id]
13 |     (get @index id)))
14 | 
15 | (defn cache [size]
16 |   (Cache. #js [] (atom {}) size))
17 | 


--------------------------------------------------------------------------------
/src/main/om/next/impl/parser.cljc:
--------------------------------------------------------------------------------
  1 | (ns
  2 |   ^{:doc "
  3 |    Generic query expression parsing and AST manipulation.
  4 | 
  5 |    QUERY EXPRESSIONS
  6 | 
  7 |    Query expressions are a variation on Datomic Pull Syntax
  8 |    http://docs.datomic.com/pull.html more suitable for generic client/server
  9 |    state transfer. It's important to note the Om Next query expression syntax is
 10 |    *not* a strict superset of Datomic Pull.
 11 | 
 12 |    A query expression is composed of EDN values. The grammar for query
 13 |    expressions follows:
 14 | 
 15 |    QueryRoot      := EdnVector(QueryExpr*)
 16 |    PlainQueryExpr := (EdnKeyword | IdentExpr | JoinExpr)
 17 |    QueryExpr      := (PlainQueryExpr | ParamExpr)
 18 |    IdentExpr      := EdnVector2(Keyword, EdnValue)
 19 |    ParamExpr      := EdnList2(PlainQueryExpr | EdnSymbol, ParamMapExpr)
 20 |    ParamMapExpr   := EdnMap(Keyword, EdnValue)
 21 |    JoinExpr       := EdnMap((Keyword | IdentExpr), (QueryRoot | UnionExpr | RecurExpr))
 22 |    UnionExpr      := EdnMap(Keyword, QueryRoot)
 23 |    RecurExpr      := ('... | Integer)
 24 | 
 25 |    Note most apis in Om Next expect a QueryRoot not a QueryExpr.
 26 | 
 27 |    QUERY EXPRESSION AST FORMAT
 28 | 
 29 |    Given a QueryExpr you can get the AST via om.next.impl.parser/expr->ast.
 30 |    The following keys can appear in the AST representation:
 31 | 
 32 |    {:type         (:prop | :join | :call | :root | :union | :union-entry)
 33 |     :key          (EdnKeyword | EdnSymbol | IdentExpr)
 34 |     :dispatch-key (EdnKeyword | EdnSymbol)
 35 |     :union-key    EdnKeyword
 36 |     :query        (QueryRoot | RecurExpr)
 37 |     :params       ParamMapExpr
 38 |     :children     EdnVector(AST)
 39 |     :component    Object
 40 |     :target       EdnKeyword}
 41 | 
 42 |    :query and :params may or may not appear. :type :call is only for
 43 |    mutations."}
 44 |   om.next.impl.parser
 45 |   (:require [clojure.set :as set]
 46 |             [om.util :as util]))
 47 | 
 48 | (declare expr->ast)
 49 | 
 50 | (defn symbol->ast [k]
 51 |   {:dispatch-key k
 52 |    :key k})
 53 | 
 54 | (defn keyword->ast [k]
 55 |   {:type :prop
 56 |    :dispatch-key k
 57 |    :key k})
 58 | 
 59 | (defn union-entry->ast [[k v]]
 60 |   (let [component (-> v meta :component)]
 61 |     (merge
 62 |       {:type :union-entry
 63 |        :union-key k
 64 |        :query v
 65 |        :children (into [] (map expr->ast) v)}
 66 |       (when-not (nil? component)
 67 |         {:component component}))))
 68 | 
 69 | (defn union->ast [m]
 70 |   {:type :union
 71 |    :query m
 72 |    :children (into [] (map union-entry->ast) m)})
 73 | 
 74 | (defn call->ast [[f args :as call]]
 75 |   (if (= 'quote f)
 76 |     (assoc (expr->ast args) :target (or (-> call meta :target) :remote))
 77 |     (let [ast (update-in (expr->ast f) [:params] merge (or args {}))]
 78 |       (cond-> ast
 79 |         (symbol? (:dispatch-key ast)) (assoc :type :call)))))
 80 | 
 81 | (defn query->ast
 82 |   "Convert a query to its AST representation."
 83 |   [query]
 84 |   (let [component (-> query meta :component)]
 85 |     (merge
 86 |       {:type :root
 87 |        :children (into [] (map expr->ast) query)}
 88 |       (when-not (nil? component)
 89 |         {:component component}))))
 90 | 
 91 | (defn join->ast [join]
 92 |   (let [query-root? (-> join meta :query-root)
 93 |         [k v] (first join)
 94 |         ast (expr->ast k)
 95 |         type (if (= :call (:type ast)) :call :join)
 96 |         component (-> v meta :component)]
 97 |     (merge ast
 98 |       {:type type :query v}
 99 |       (when-not (nil? component)
100 |         {:component component})
101 |       (when query-root?
102 |         {:query-root true})
103 |       (when-not (or (number? v) (= '... v))
104 |         (cond
105 |           (vector? v) {:children (into [] (map expr->ast) v)}
106 |           (map? v) {:children [(union->ast v)]}
107 |           :else (throw
108 |                   (ex-info (str "Invalid join, " join)
109 |                     {:type :error/invalid-join})))))))
110 | 
111 | (defn ident->ast [[k id :as ref]]
112 |   {:type :prop
113 |    :dispatch-key k
114 |    :key ref})
115 | 
116 | (defn expr->ast
117 |   "Given a query expression convert it into an AST."
118 |   [x]
119 |   (cond
120 |     (symbol? x)  (symbol->ast x)
121 |     (keyword? x) (keyword->ast x)
122 |     (map? x)     (join->ast x)
123 |     (vector? x)  (ident->ast x)
124 |     (seq? x)     (call->ast x)
125 |     :else        (throw
126 |                    (ex-info (str "Invalid expression " x)
127 |                      {:type :error/invalid-expression}))))
128 | 
129 | (defn wrap-expr [root? expr]
130 |   (if root?
131 |     (with-meta
132 |       (cond-> expr (keyword? expr) list)
133 |       {:query-root true})
134 |     expr))
135 | 
136 | (defn parameterize [expr params]
137 |   (if-not (empty? params)
138 |     (list expr params)
139 |     (list expr)))
140 | 
141 | (defn ast->expr
142 |   "Given a query expression AST convert it back into a query expression."
143 |   ([ast]
144 |     (ast->expr ast false))
145 |   ([{:keys [type component] :as ast} unparse?]
146 |    (if (= :root type)
147 |      (cond-> (into [] (map #(ast->expr % unparse?)) (:children ast))
148 |        (not (nil? component)) (with-meta {:component component}))
149 |      (let [{:keys [key query query-root params]} ast]
150 |        (wrap-expr query-root
151 |          (if (and params (not= :call type))
152 |            (let [expr (ast->expr (dissoc ast :params) unparse?)]
153 |              (parameterize expr params))
154 |            (let [key (if (= :call type) (parameterize key params) key)]
155 |              (if (or (= :join type)
156 |                      (and (= :call type) (:children ast)))
157 |                (if (and (not= '... query) (not (number? query))
158 |                         (or (true? unparse?)
159 |                             (= :call type)))
160 |                  (let [{:keys [children]} ast]
161 |                    (if (and (== 1 (count children))
162 |                             (= :union (:type (first children)))) ;; UNION
163 |                      {key (into (cond-> {}
164 |                                   component (with-meta {:component component}))
165 |                                 (map (fn [{:keys [union-key children component]}]
166 |                                        [union-key
167 |                                         (cond-> (into [] (map #(ast->expr % unparse?)) children)
168 |                                           (not (nil? component)) (with-meta {:component component}))]))
169 |                                 (:children (first children)))}
170 |                      {key (cond-> (into [] (map #(ast->expr % unparse?)) children)
171 |                             (not (nil? component)) (with-meta {:component component}))}))
172 |                  {key query})
173 |                key))))))))
174 | 
175 | (defn path-meta
176 |   "Add path metadata to a data structure. data is the data to be worked on.
177 |    path is the current path into the data. query is the query used to
178 |    walk the data. union-expr tracks the last seen union query to be used
179 |    when it finds a recursive union."
180 |   ([data path query]
181 |    (path-meta data path query nil))
182 |   ([data path query union-expr]
183 |    (cond
184 |      (nil? query)
185 |      (cond-> data
186 |        #?(:clj  (instance? clojure.lang.IObj data)
187 |           :cljs (satisfies? IWithMeta data))
188 |        (vary-meta assoc :om-path path))
189 | 
190 |      (sequential? data)
191 |      (-> (into []
192 |            (map-indexed
193 |              (fn [idx v]
194 |                (path-meta v (conj path idx) query union-expr))) data)
195 |        (vary-meta assoc :om-path path))
196 | 
197 |      (vector? query)
198 |      (loop [joins (seq query) ret data]
199 |        (if-not (nil? joins)
200 |          (let [join (first joins)]
201 |            (if-not (or (util/join? join)
202 |                        (util/ident? join)
203 |                        (and (seq? join)
204 |                          (util/ident? (first join))))
205 |              (recur (next joins) ret)
206 |              (let [join        (cond-> join (seq? join) first)
207 |                    join        (cond-> join (util/ident? join) (hash-map '[*]))
208 |                    [key sel]   (util/join-entry join)
209 |                    union-entry (if (util/union? join) sel union-expr)
210 |                    sel         (if (util/recursion? sel)
211 |                                  (if-not (nil? union-expr)
212 |                                    union-entry
213 |                                    query)
214 |                                  sel)
215 |                    key         (cond-> key (util/unique-ident? key) first)
216 |                    v           (get ret key)]
217 |                (recur (next joins)
218 |                  (cond-> ret
219 |                    (and (map? ret) (contains? ret key))
220 |                    (assoc key
221 |                      (path-meta v (conj path key) sel union-entry)))))))
222 |          (cond-> ret
223 |            #?(:clj  (instance? clojure.lang.IObj ret)
224 |               :cljs (satisfies? IWithMeta ret))
225 |            (vary-meta assoc :om-path path))))
226 | 
227 |      :else
228 |      ;; UNION
229 |      (if (map? data)
230 |        (let [dispatch-key (comp :dispatch-key expr->ast)
231 |              branches     (vals query)
232 |              props (map dispatch-key (keys data))
233 |              query (reduce (fn [ret q]
234 |                              (let [query-props (into #{} (map dispatch-key) q)
235 |                                    props (set props)]
236 |                                (cond
237 |                                  (= (set props)
238 |                                     (set query-props)) (reduced q)
239 |                                  (set/subset? props query-props) q
240 |                                  :else ret)))
241 |                      nil branches)]
242 |          (path-meta data path query union-expr))
243 |        data))))
244 | 
245 | (defn rethrow? [x]
246 |   (and (instance? #?(:clj clojure.lang.ExceptionInfo :cljs ExceptionInfo) x)
247 |        (= :om.next/abort (-> x ex-data :type))))
248 | 
249 | (defn parser
250 |   "Given a :read and/or :mutate function return a parser. Refer to om.next/parser
251 |    for top level documentation."
252 |   [{:keys [read mutate] :as config}]
253 |   (fn self
254 |     ([env query] (self env query nil))
255 |     ([env query target]
256 |      (let [elide-paths? (or (:elide-paths config) (:query-root env))
257 |            {:keys [path] :as env}
258 |            (cond-> (assoc env :parser self :target target :query-root :om.next/root)
259 |              (not (contains? env :path)) (assoc :path []))]
260 |        (letfn [(step [ret expr]
261 |                  (let [{query' :query :keys [key dispatch-key params] :as ast} (expr->ast expr)
262 |                        env   (cond-> (merge env {:ast ast :query query'})
263 |                                (nil? query')   (dissoc :query)
264 |                                (= '... query') (assoc :query query)
265 |                                (vector? key)   (assoc :query-root key))
266 |                        type  (:type ast)
267 |                        call? (= :call type)
268 |                        res   (case type
269 |                                :call
270 |                                (do
271 |                                  (assert mutate "Parse mutation attempted but no :mutate function supplied")
272 |                                  (mutate env dispatch-key params))
273 |                                (:prop :join :union)
274 |                                (do
275 |                                  (assert read "Parse read attempted but no :read function supplied")
276 |                                  (read env dispatch-key params)))]
277 |                    (if-not (nil? target)
278 |                      (let [ast' (get res target)]
279 |                        (cond-> ret
280 |                          (true? ast') (conj expr)
281 |                          (map? ast') (conj (ast->expr ast'))))
282 |                      (if-not (or call? (nil? (:target ast)) (contains? res :value))
283 |                        ret
284 |                        (let [error   (atom nil)
285 |                              mut-ret (atom nil)]
286 |                          (when (and call? (not (nil? (:action res))))
287 |                            (try
288 |                              (reset! mut-ret ((:action res)))
289 |                              (catch #?(:clj Throwable :cljs :default) e
290 |                                (if (rethrow? e)
291 |                                  (throw e)
292 |                                  (reset! error e)))))
293 |                          (let [value (:value res)]
294 |                            (when call?
295 |                              (assert (or (nil? value) (map? value))
296 |                                (str dispatch-key " mutation :value must be nil or a map with structure {:keys [...]}")))
297 |                            (cond-> ret
298 |                              (not (nil? value)) (assoc (cond-> key
299 |                                                          (util/unique-ident? key)
300 |                                                          first)
301 |                                                   value)
302 |                              @mut-ret (assoc-in [key :result] @mut-ret)
303 |                              @error (assoc key {:om.next/error @error}))))))))]
304 |          (cond-> (reduce step (if (nil? target) {} []) query)
305 |            (and (nil? target) (not elide-paths?)) (path-meta path query)))))))
306 | 
307 | (defn dispatch [_ k _] k)
308 | 


--------------------------------------------------------------------------------
/src/main/om/next/protocols.cljc:
--------------------------------------------------------------------------------
 1 | (ns om.next.protocols)
 2 | 
 3 | (defprotocol IIndexer
 4 |   (indexes [this])
 5 |   (index-root [this x])
 6 |   (index-component! [this component])
 7 |   (drop-component! [this component])
 8 |   (ref-for [this component])
 9 |   (key->components [this k]))
10 | 
11 | (defprotocol IReconciler
12 |   (basis-t [this])
13 |   (add-root! [reconciler root-class target options])
14 |   (remove-root! [reconciler target])
15 |   (schedule-render! [reconciler])
16 |   (schedule-sends! [reconciler])
17 |   (queue! [reconciler ks] [reconciler ks remote])
18 |   (queue-sends! [reconciler sends])
19 |   (reindex! [reconciler])
20 |   (reconcile! [reconciler] [reconciler remote])
21 |   (send! [reconciler]))
22 | 
23 | #?(:clj
24 |    (defprotocol IReactDOMElement
25 |      (^String -render-to-string [this react-id ^StringBuilder sb] "renders a DOM node to string.")))
26 | 
27 | #?(:clj
28 |    (defprotocol IReactComponent
29 |      (-render [this] "must return a valid ReactDOMElement.")))
30 | 
31 | #?(:clj
32 |    (defprotocol IReactLifecycle
33 |      (shouldComponentUpdate [this next-props next-state])
34 |      (initLocalState [this])
35 |      (componentWillReceiveProps [this next-props])
36 |      (componentWillUpdate [this next-props next-state])
37 |      (componentDidUpdate [this prev-props prev-state])
38 |      (componentWillMount [this])
39 |      (componentDidMount [this])
40 |      (componentWillUnmount [this])
41 |      (render [this])))
42 | 


--------------------------------------------------------------------------------
/src/main/om/next/server.clj:
--------------------------------------------------------------------------------
 1 | (ns om.next.server
 2 |   (:require [om.next.impl.parser :as parser]
 3 |             [om.transit :as transit]))
 4 | 
 5 | (defn parser
 6 |   "Create a parser. The argument is a map of two keys, :read and :mutate. Both
 7 |    functions should have the signature (Env -> Key -> Params -> ParseResult)."
 8 |   [opts]
 9 |   (parser/parser (assoc opts :elide-paths true)))
10 | 
11 | (defn dispatch
12 |   "Helper function for implementing :read and :mutate as multimethods. Use this
13 |    as the dispatch-fn."
14 |   [_ key _] key)
15 | 
16 | (defn reader
17 |   "Create a Om Next transit reader. This reader can handler the tempid type.
18 |    Can pass transit reader customization opts map."
19 |   ([in] (transit/reader in))
20 |   ([in opts] (transit/reader in opts)))
21 | 
22 | (defn writer
23 |   "Create a Om Next transit reader. This writer can handler the tempid type.
24 |    Can pass transit writer customization opts map."
25 |   ([out] (transit/writer out))
26 |   ([out opts] (transit/writer out opts)))
27 | 


--------------------------------------------------------------------------------
/src/main/om/tempid.cljc:
--------------------------------------------------------------------------------
 1 | (ns om.tempid
 2 |   #?(:clj (:import [java.io Writer])))
 3 | 
 4 | ;; =============================================================================
 5 | ;; ClojureScript
 6 | 
 7 | #?(:cljs
 8 |    (deftype TempId [^:mutable id ^:mutable __hash]
 9 |      Object
10 |      (toString [this]
11 |        (pr-str this))
12 |      IEquiv
13 |      (-equiv [this other]
14 |        (and (instance? TempId other)
15 |             (= (. this -id) (. other -id))))
16 |      IHash
17 |      (-hash [this]
18 |        (when (nil? __hash)
19 |          (set! __hash (hash id)))
20 |        __hash)
21 |      IPrintWithWriter
22 |      (-pr-writer [_ writer _]
23 |        (write-all writer "#om/id[\"" id "\"]"))))
24 | 
25 | #?(:cljs
26 |    (defn tempid
27 |      ([]
28 |       (tempid (random-uuid)))
29 |      ([id]
30 |       (TempId. id nil))))
31 | 
32 | ;; =============================================================================
33 | ;; Clojure
34 | 
35 | #?(:clj
36 |    (defrecord TempId [id]
37 |      Object
38 |      (toString [this]
39 |        (pr-str this))))
40 | 
41 | #?(:clj
42 |    (defmethod print-method TempId [^TempId x ^Writer writer]
43 |      (.write writer (str "#om/id[\"" (.id x) "\"]"))))
44 | 
45 | #?(:clj
46 |    (defn tempid
47 |      ([]
48 |       (tempid (java.util.UUID/randomUUID)))
49 |      ([uuid]
50 |       (TempId. uuid))))
51 | 
52 | (defn tempid?
53 |   #?(:cljs {:tag boolean})
54 |   [x]
55 |   (instance? TempId x))
56 | 


--------------------------------------------------------------------------------
/src/main/om/transit.cljc:
--------------------------------------------------------------------------------
 1 | (ns om.transit
 2 |   #?(:clj (:refer-clojure :exclude [ref]))
 3 |   (:require [cognitect.transit :as t]
 4 |    #?(:cljs [com.cognitect.transit :as ct])
 5 |             [om.tempid :as tempid #?@(:cljs [:refer [TempId]])])
 6 |   #?(:clj (:import [com.cognitect.transit
 7 |                     TransitFactory WriteHandler ReadHandler]
 8 |                    [om.tempid TempId])))
 9 | 
10 | #?(:cljs
11 |    (deftype TempIdHandler []
12 |      Object
13 |      (tag [_ _] "om/id")
14 |      (rep [_ r] (. r -id))
15 |      (stringRep [_ _] nil)))
16 | 
17 | #?(:clj
18 |    (deftype TempIdHandler []
19 |      WriteHandler
20 |      (tag [_ _] "om/id")
21 |      (rep [_ r] (. ^TempId r -id))
22 |      (stringRep [_ r] (. ^TempId r -id))
23 |      (getVerboseHandler [_] nil)))
24 | 
25 | #?(:cljs
26 |    (defn writer
27 |      ([]
28 |       (writer {}))
29 |      ([opts]
30 |       (t/writer :json
31 |         (assoc-in opts [:handlers TempId] (TempIdHandler.))))))
32 | 
33 | #?(:clj
34 |    (defn writer
35 |      ([out]
36 |       (writer out {}))
37 |      ([out opts]
38 |       (t/writer out :json
39 |         (assoc-in opts [:handlers TempId] (TempIdHandler.))))))
40 | 
41 | #?(:cljs
42 |    (defn reader
43 |      ([]
44 |       (reader {}))
45 |      ([opts]
46 |       (t/reader :json
47 |         (assoc-in opts
48 |           [:handlers "om/id"]
49 |           (fn [id] (tempid/tempid id)))))))
50 | 
51 | #?(:clj
52 |    (defn reader
53 |      ([in]
54 |       (reader in {}))
55 |      ([in opts]
56 |       (t/reader in :json
57 |         (assoc-in opts
58 |           [:handlers "om/id"]
59 |           (reify
60 |             ReadHandler
61 |             (fromRep [_ id] (TempId. id))))))))
62 | 
63 | (comment
64 |   ;; cljs
65 |   (t/read (reader) (t/write (writer) (tempid/tempid)))
66 | 
67 |   ;; clj
68 |   (import '[java.io ByteArrayOutputStream ByteArrayInputStream])
69 | 
70 |   (def baos (ByteArrayOutputStream. 4096))
71 |   (def w (writer baos))
72 |   (t/write w (TempId. (java.util.UUID/randomUUID)))
73 |   (.toString baos)
74 | 
75 |   (def in (ByteArrayInputStream. (.toByteArray baos)))
76 |   (def r (reader in))
77 |   (t/read r)
78 |   )


--------------------------------------------------------------------------------
/src/main/om/util.cljc:
--------------------------------------------------------------------------------
 1 | (ns om.util
 2 |   (:refer-clojure :exclude [ident?]))
 3 | 
 4 | (defn force-children [x]
 5 |   (cond->> x
 6 |     (seq? x) (into [] (map force-children))))
 7 | 
 8 | (defn union?
 9 |   #?(:cljs {:tag boolean})
10 |   [expr]
11 |   (let [expr (cond-> expr (seq? expr) first)]
12 |     (and (map? expr)
13 |       (map? (-> expr first second)))))
14 | 
15 | (defn join?
16 |   #?(:cljs {:tag boolean})
17 |   [x]
18 |   (let [x (if (seq? x) (first x) x)]
19 |     (map? x)))
20 | 
21 | (defn ident?
22 |   "Returns true if x is an ident."
23 |   #?(:cljs {:tag boolean})
24 |   [x]
25 |   (and (vector? x)
26 |     (== 2 (count x))
27 |     (keyword? (nth x 0))))
28 | 
29 | (defn join-entry [expr]
30 |   (if (seq? expr)
31 |     (ffirst expr)
32 |     (first expr)))
33 | 
34 | (defn join-key [expr]
35 |   (cond
36 |     (map? expr) (ffirst expr)
37 |     (seq? expr) (join-key (first expr))
38 |     :else       expr))
39 | 
40 | (defn join-value [join]
41 |   (second (join-entry join)))
42 | 
43 | (defn unique-ident?
44 |   #?(:cljs {:tag boolean})
45 |   [x]
46 |   (and (ident? x) (= '_ (second x))))
47 | 
48 | (defn recursion?
49 |   #?(:cljs {:tag boolean})
50 |   [x]
51 |   (or #?(:clj (= '... x)
52 |          :cljs (symbol-identical? '... x))
53 |       (number? x)))
54 | 
55 | (defn mutation?
56 |   #?(:cljs {:tag boolean})
57 |   [expr]
58 |   (let [expr (cond-> expr (seq? expr) first)]
59 |     (symbol? expr)))
60 | 
61 | (defn mutation-key [expr]
62 |   {:pre [(symbol? (first expr))]}
63 |   (first expr))
64 | 


--------------------------------------------------------------------------------
/src/test/om/checksums_test.clj:
--------------------------------------------------------------------------------
 1 | (ns om.checksums-test
 2 |   (:require [clojure.test :refer [deftest testing is are]]
 3 |             [om.test-utils :refer [remove-whitespace]]
 4 |             [om.checksums :as chk]))
 5 | 
 6 | (deftest test-checksum
 7 |   (are [data res] (= (chk/adler32 (StringBuilder. data)) res)
 8 |     "<div data-reactid=\".p55bcrvgg0\"></div>" -47641439
 9 |     "<div data-reactid=\".0\" id=\"foo\">Hello World</div>" -1847259110
10 |     (remove-whitespace "<div data-reactid=\".0\">
11 |                           <div data-reactid=\".0.0\">
12 |                             <button data-reactid=\".0.0.0\">Load children</button>
13 |                             <ul data-reactid=\".0.0.1\">
14 |                               <li data-reactid=\".0.0.1.0\">1</li>
15 |                               <li data-reactid=\".0.0.1.1\">2</li>
16 |                               <li data-reactid=\".0.0.1.2\">3</li>
17 |                             </ul>
18 |                           </div>
19 |                         </div>") 821447442
20 |     "Lorem ipsum dolor sit amet, ea nam mutat probatus. Erat volutpat liberavisse eu his,
21 |  id qui eius congue accumsan. Pro erat natum ponderum ut. Vidit assum eu eam. Mei animal
22 |  epicurei facilisi te. In eum euismod principes, id soluta volutpat pri. Nulla harum ex has,
23 |  aliquam verterem recteque has eu. Cu noster utamur quaestio quo, eos eius diceret ei. Ponderum
24 |  atomorum has et. Mel amet dolores philosophia ut, eam id erat noluisse postulant. Sit vide
25 |  regione eu. Ne vis justo liber." -1507348871))
26 | 
27 | (deftest test-assign-react-checksum
28 |   (is (= (str (chk/assign-react-checksum (StringBuilder. "<div data-reactid=\".p55bcrvgg0\"></div>")))
29 |          "<div data-reactid=\".p55bcrvgg0\" data-react-checksum=\"-47641439\"></div>"))
30 |   (is (= (str (chk/assign-react-checksum (StringBuilder. "<div data-reactid=\".0\" id=\"foo\">Hello World</div>")))
31 |          "<div data-reactid=\".0\" id=\"foo\" data-react-checksum=\"-1847259110\">Hello World</div>")))
32 | 


--------------------------------------------------------------------------------
/src/test/om/dom_test.clj:
--------------------------------------------------------------------------------
  1 | (ns om.dom-test
  2 |   (:refer-clojure :exclude [read])
  3 |   (:require [clojure.test :refer [deftest testing is are]]
  4 |             [om.test-utils :refer [remove-whitespace]]
  5 |             [om.next :as om :refer [defui]]
  6 |             [om.dom :as dom]
  7 |             [om.next.protocols :as p]))
  8 | 
  9 | (defn test-tags [tags res-fn]
 10 |   `(are [element# res#] (let [sb# (StringBuilder.)]
 11 |                           (dom/render-element! {:tag element#} (volatile! 1) sb#)
 12 |                           (= (str sb#) res#))
 13 |      ~@(mapcat (fn [tag#] [tag# (res-fn tag#)]) tags)))
 14 | 
 15 | (defmacro test-container-tags []
 16 |   (let [container-tags (->> dom/tags
 17 |                          (map str)
 18 |                          (filter #(dom/container-tag? % nil)))]
 19 |     (test-tags container-tags #(str "<" % " data-reactroot=\"\" data-reactid=\"1\">" "</" % ">"))))
 20 | 
 21 | (defmacro test-void-tags []
 22 |   (let [container-tags (->> dom/tags
 23 |                          (map str)
 24 |                          (filter #(not (dom/container-tag? % nil))))]
 25 |     (test-tags container-tags #(str "<" % " data-reactroot=\"\" data-reactid=\"1\"/>"))))
 26 | 
 27 | (defn simple-component []
 28 |   (dom/div nil "Hello World"))
 29 | 
 30 | (defn simple-nested-component []
 31 |   (dom/div nil
 32 |     (dom/h1 #js {:id "page-title"} "Title")))
 33 | 
 34 | (defn comp-nested-component []
 35 |   (dom/div nil
 36 |     (simple-component)
 37 |     (simple-nested-component)))
 38 | 
 39 | (deftest test-render-element
 40 |   (testing "render-element works with empty content in all tags"
 41 |     (test-container-tags)
 42 |     (test-void-tags))
 43 |   (testing "render-element renders simple function elements"
 44 |     (are [component res] (let [sb (StringBuilder.)]
 45 |                            (dom/render-element! component (volatile! 1) sb)
 46 |                            (= (str sb) res))
 47 |       (simple-component) "<div data-reactroot=\"\" data-reactid=\"1\">Hello World</div>"
 48 |       (simple-nested-component) (remove-whitespace
 49 |                                   "<div data-reactroot=\"\" data-reactid=\"1\">
 50 |                                      <h1 id=\"page-title\" data-reactid=\"2\">Title</h1>
 51 |                                    </div>")
 52 |       (comp-nested-component) (remove-whitespace
 53 |                                 "<div data-reactroot=\"\" data-reactid=\"1\">
 54 |                                    <div data-reactid=\"2\">Hello World</div>
 55 |                                    <div data-reactid=\"3\">
 56 |                                      <h1 id=\"page-title\" data-reactid=\"4\">Title</h1>
 57 |                                    </div>
 58 |                                  </div>"))))
 59 | 
 60 | (defui SimpleComponent
 61 |   Object
 62 |   (render [this]
 63 |     (dom/div nil "Hello World")))
 64 | 
 65 | (defui Hello
 66 |   Object
 67 |   (render [this]
 68 |     (dom/p nil (-> this om/props :text))))
 69 | 
 70 | (defui Children
 71 |   Object
 72 |   (render [this]
 73 |     (dom/div nil
 74 |       (map identity
 75 |         #js [(dom/div nil "Foo")
 76 |              (dom/div nil "Bar")
 77 |              (map identity
 78 |                #js [(dom/div nil "Bar")
 79 |                     (dom/div nil "Woz")])]))))
 80 | 
 81 | (deftest test-render-to-str
 82 |   (let [c ((om/factory SimpleComponent))]
 83 |     (is (= (str (#'dom/render-to-str* c)) "<div data-reactroot=\"\" data-reactid=\"1\">Hello World</div>")))
 84 |   (let [hello (om/factory Hello)]
 85 |     (is (= (str (#'dom/render-to-str* (hello {:text "Hello, world!"})))
 86 |            "<p data-reactroot=\"\" data-reactid=\"1\">Hello, world!</p>")))
 87 |   (let [children (om/factory Children)]
 88 |     (is (= (str (#'dom/render-to-str* (children)))
 89 |           (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
 90 |                                   <div data-reactid=\"2\">Foo</div>
 91 |                                   <div data-reactid=\"3\">Bar</div>
 92 |                                   <div data-reactid=\"4\">Bar</div>
 93 |                                   <div data-reactid=\"5\">Woz</div>
 94 |                               </div>")))))
 95 | 
 96 | (deftest test-format-react-attrs
 97 |   (are [map res] (let [sb (StringBuilder.)]
 98 |                    (dom/render-attr-map! sb "div" map)
 99 |                    (= (str sb) res))
100 |     {:htmlFor "something"} " for=\"something\""
101 |     {:className "foo"} " class=\"foo\""
102 |     {:srcLang "en"} " srclang=\"en\""
103 |     {:acceptCharset "ISO-8859-1"} " accept-charset=\"ISO-8859-1\""
104 |     {:placeholder "Title"} " placeholder=\"Title\""
105 |     ;; svg xlink:stuff
106 |     {:xlinkActuate "foo"} " xlink:actuate=\"foo\""))
107 | 
108 | (deftest test-ref-is-elided-in-props
109 |   (let [sb (StringBuilder.)]
110 |     (dom/render-element! (dom/div #js {:ref "someDiv"}) (volatile! 1) sb)
111 |     (is (= (str sb)
112 |           "<div data-reactroot=\"\" data-reactid=\"1\"></div>"))))
113 | 
114 | (deftest test-attrs-rendered-in-declaration-order
115 |   (are [element res] (let [sb (StringBuilder.)]
116 |                        (dom/render-element! element (volatile! 1) sb)
117 |                        (= (str sb) res))
118 |     (dom/input {:type "text"
119 |                 :placeholder "some text"
120 |                 :id "stuff"})
121 |     "<input type=\"text\" placeholder=\"some text\" id=\"stuff\" data-reactroot=\"\" data-reactid=\"1\"/>"
122 | 
123 |     (dom/input {:id "stuff"
124 |                 :placeholder "some text"
125 |                 :type "text"})
126 |     "<input type=\"text\" id=\"stuff\" placeholder=\"some text\" data-reactroot=\"\" data-reactid=\"1\"/>"
127 | 
128 |     (dom/input {:placeholder "some text"
129 |                 :id "stuff"
130 |                 :type "text"})
131 |     "<input type=\"text\" placeholder=\"some text\" id=\"stuff\" data-reactroot=\"\" data-reactid=\"1\"/>"))
132 | 
133 | (deftest test-only-supported-attrs-rendered
134 |   (are [element markup] (= (str (#'dom/render-to-str* element)) (remove-whitespace markup))
135 |     (dom/div #js {:not-supported "foo"}) "<div data-reactroot=\"\" data-reactid=\"1\"></div>"
136 |     (dom/div {:className "stuff" :class "other"}) "<div class=\"stuff\" data-reactroot=\"\" data-reactid=\"1\"></div>"
137 |     (dom/div {:media :stuff}) "<div data-reactroot=\"\" data-reactid=\"1\"></div>"
138 |     (dom/div {:data-foo "foo"}) "<div data-foo=\"foo\" data-reactroot=\"\" data-reactid=\"1\"></div>"
139 |     (dom/div {:foo true}) "<div data-reactroot=\"\" data-reactid=\"1\"></div>"
140 |     (dom/div {:autoFocus true}) "<div autofocus data-reactroot=\"\" data-reactid=\"1\"></div>"
141 |     (dom/div #js {:autoCapitalize true
142 |                   :color "tomato"
143 |                   :itemScope 1
144 |                   :keyParams true})
145 |     "<div autocapitalize color=\"tomato\" itemscope=\"1\" keyparams data-reactroot=\"\" data-reactid=\"1\"></div>"
146 |     (dom/svg #js {:panose1 "stuff"}) "<svg panose-1=\"stuff\" data-reactroot=\"\" data-reactid=\"1\"></svg>"))
147 | 
148 | 
149 | (def styles
150 |   #js {:textAlign "center"
151 |        :marginLeft "10px"})
152 | 
153 | (defui ComponentWithStyle
154 |   Object
155 |   (render [this]
156 |     (dom/div #js {:style styles})))
157 | 
158 | (deftest test-normalize-styles
159 |   (are [styles result] (let [sb (StringBuilder.)]
160 |                          (dom/normalize-styles! sb styles)
161 |                          (= (str sb) result))
162 |     (select-keys styles [:textAlign]) "text-align:center;"
163 |     styles "text-align:center;margin-left:10px;"
164 |     {:zoom 1} "zoom:1;"
165 |     {:zoom 1
166 |      :opacity 0.5
167 |      :width 100} "zoom:1;opacity:0.5;width:100px;"))
168 | 
169 | (deftest test-empty-styles-not-rendered
170 |   (let [sb (StringBuilder.)]
171 |     (dom/render-element! (dom/div {:style {}}) (volatile! 1) sb)
172 |     (is (= (str sb)
173 |           "<div data-reactroot=\"\" data-reactid=\"1\"></div>"))))
174 | 
175 | (deftest test-render-component-with-style
176 |   (let [ctor (om/factory ComponentWithStyle)]
177 |     (is (= (str (#'dom/render-to-str* (ctor)))
178 |           "<div style=\"text-align:center;margin-left:10px;\" data-reactroot=\"\" data-reactid=\"1\"></div>"))))
179 | 
180 | ;; Simple nested `defui`s
181 | 
182 | (defui SimpleNestedChild
183 |   Object
184 |   (render [this]
185 |     (dom/div nil "child")))
186 | 
187 | (def simple-nested-child-factory (om/factory SimpleNestedChild))
188 | 
189 | (defui SimpleNestedParent
190 |   Object
191 |   (render [this]
192 |     (dom/div nil
193 |       (simple-nested-child-factory))))
194 | 
195 | (deftest test-simple-nested-defuis
196 |   (let [ctor (om/factory SimpleNestedParent)]
197 |     (is (= (str (#'dom/render-to-str* (ctor)))
198 |            (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
199 |                                    <div data-reactid=\"2\">child</div>
200 |                                </div>")))))
201 | 
202 | 
203 | ;; Om Simple Recursive Tree
204 | (def simple-tree-data
205 |   {:tree {:node-value 1
206 |           :children [{:node-value 2
207 |                       :children [{:node-value 3
208 |                                   :children []}]}
209 |                      {:node-value 4
210 |                       :children []}]}})
211 | 
212 | (declare simple-node)
213 | 
214 | (defui SimpleNode
215 |   static om/IQuery
216 |   (query [this]
217 |     '[:node-value {:children ...}])
218 |   Object
219 |   (render [this]
220 |     (let [{:keys [node-value children]} (om/props this)]
221 |       (dom/li nil
222 |         (dom/div nil (str "Node value:" node-value))
223 |         (dom/ul nil
224 |           (map simple-node children))))))
225 | 
226 | (def simple-node (om/factory SimpleNode))
227 | 
228 | (defui SimpleTree
229 |   static om/IQuery
230 |   (query [this]
231 |     [{:tree (om/get-query SimpleNode)}])
232 |   Object
233 |   (render [this]
234 |     (let [{:keys [tree]} (om/props this)]
235 |       (dom/ul nil
236 |         (simple-node tree)))))
237 | 
238 | (defmulti simple-tree-read om/dispatch)
239 | 
240 | (defmethod simple-tree-read :node-value
241 |   [{:keys [data] :as env} _ _]
242 |   {:value (:node-value data)})
243 | 
244 | (defmethod simple-tree-read :children
245 |   [{:keys [data parser query] :as env} _ _]
246 |   {:value (let [f #(parser (assoc env :data %) query)]
247 |             (into [] (map f (:children data))))})
248 | 
249 | (defmethod simple-tree-read :tree
250 |   [{:keys [state parser query] :as env} k _]
251 |   (let [st @state]
252 |     {:value (parser (assoc env :data (:tree st)) query)}))
253 | 
254 | (def simple-tree-reconciler
255 |   (om/reconciler
256 |     {:state     (atom simple-tree-data)
257 |      :normalize false
258 |      :parser    (om/parser {:read simple-tree-read})}))
259 | 
260 | (deftest test-render-simple-recursive-example
261 |   (let [c (om/add-root! simple-tree-reconciler SimpleTree nil)]
262 |     (is (= (str (#'dom/render-to-str* c))
263 |            (remove-whitespace
264 |              "<ul data-reactroot=\"\" data-reactid=\"1\">
265 |                   <li data-reactid=\"2\">
266 |                     <div data-reactid=\"3\">Node value:1</div>
267 |                     <ul data-reactid=\"4\">
268 |                       <li data-reactid=\"5\">
269 |                         <div data-reactid=\"6\">Node value:2</div>
270 |                         <ul data-reactid=\"7\">
271 |                           <li data-reactid=\"8\">
272 |                             <div data-reactid=\"9\">Node value:3</div>
273 |                             <ul data-reactid=\"10\"></ul>
274 |                           </li>
275 |                         </ul>
276 |                       </li>
277 |                       <li data-reactid=\"11\">
278 |                         <div data-reactid=\"12\">Node value:4</div>
279 |                         <ul data-reactid=\"13\"></ul>
280 |                       </li>
281 |                     </ul>
282 |                   </li>
283 |               </ul>")))))
284 | 
285 | (defn MultipleTextChildren []
286 |   (dom/div nil
287 |     "Some text"
288 |     "More text"))
289 | 
290 | (defn ChildAndText []
291 |   (dom/div nil
292 |     (dom/p nil "A paragraph!")
293 |     "More text"))
294 | 
295 | (deftest test-render-multiple-text-children
296 |   (testing "rendering an element with multiple children converts text nodes to <span>"
297 |     (are [comp res] (let [sb (StringBuilder.)]
298 |                       (dom/render-element! (comp) (volatile! 1) sb)
299 |                       (= (str sb)
300 |                          (remove-whitespace res)))
301 |       MultipleTextChildren "<div data-reactroot=\"\" data-reactid=\"1\">
302 |                               <!-- react-text: 2 -->Some text<!-- /react-text -->
303 |                               <!-- react-text: 3 -->More text<!-- /react-text -->
304 |                             </div>"
305 |       ChildAndText "<div data-reactroot=\"\" data-reactid=\"1\">
306 |                       <p data-reactid=\"2\">A paragraph!</p>
307 |                       <!-- react-text: 3 -->More text<!-- /react-text -->
308 |                     </div>")))
309 | 
310 | ;; Shared test
311 | 
312 | (defui Home
313 |   static om/IQuery
314 |   (query [this] [:counter])
315 | 
316 |   Object
317 |   (render [this]
318 |     (let [shared (om/shared this)
319 |           props  (om/props this)]
320 |       (dom/div nil
321 |         (dom/h3 nil (str "Props: " props))
322 |         (dom/h3 nil (str "Shared: " shared))
323 |         (dom/button
324 |           #js {:onClick #(om/transact! this '[(my/test) :counter])}
325 |           "Increment!")))))
326 | 
327 | (def app-state (atom {:counter 0}))
328 | 
329 | (defn read
330 |   [env key params]
331 |   (let [{:keys [state]} env]
332 |     {:value (get @state key)}))
333 | 
334 | (defn mutate
335 |   [env key params]
336 |   (let [{:keys [state]} env]
337 |     {:value  {:keys [:counter]}
338 |      :action #(swap! state update-in [:counter] inc)}))
339 | 
340 | (def reconciler
341 |   (om/reconciler
342 |     {:state     app-state
343 |      :parser    (om/parser {:read read :mutate mutate})
344 |      :shared    {}
345 |      :shared-fn (fn [root-props]
346 |                   root-props)}))
347 | 
348 | (deftest test-shared
349 |   (let [c (om/add-root! reconciler Home nil)]
350 |     (is (= (str (#'dom/render-to-str* c))
351 |            (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
352 |                                    <h3 data-reactid=\"2\">Props: {:counter 0}</h3>
353 |                                    <h3 data-reactid=\"3\">Shared: {:counter 0}</h3>
354 |                                    <button data-reactid=\"4\">Increment!</button>
355 |                                </div>")))))
356 | 
357 | (deftest test-render-to-str-elements
358 |   (are [elem res] (= (str (#'dom/render-to-str* elem)) res)
359 |     (dom/div nil "foo") "<div data-reactroot=\"\" data-reactid=\"1\">foo</div>"))
360 | 
361 | (deftest react-key-in-elements
362 |   (is (= (:react-key (dom/div {:key "foo"})) "foo"))
363 |   (is (= (:attrs (dom/div {:key "foo"})) {}))
364 |   (is (= (:react-key (dom/div nil)) nil))
365 |   (is (= (str (#'dom/render-to-str* (dom/div {:key "foo"})))
366 |         "<div data-reactroot=\"\" data-reactid=\"1\"></div>"))
367 |   (is (= (str (#'dom/render-to-str* (dom/div nil (dom/div #js {:key "foo"}))))
368 |         "<div data-reactroot=\"\" data-reactid=\"1\"><div data-reactid=\"2\"></div></div>")))
369 | 
370 | (deftest test-non-string-attributes
371 |   (is (= (str (#'dom/render-to-str* (dom/div {:className 3})))
372 |         "<div class=\"3\" data-reactroot=\"\" data-reactid=\"1\"></div>")))
373 | 
374 | (defui NilChild
375 |   Object
376 |   (render [this]
377 |     nil))
378 | 
379 | (def nil-child-factory (om/factory NilChild))
380 | 
381 | (defui NilParent
382 |   Object
383 |   (render [this]
384 |     (dom/div nil
385 |       "foo"
386 |       (nil-child-factory)
387 |       "bar")))
388 | 
389 | (defui NilChildrenComp
390 |   Object
391 |   (render [this]
392 |     (dom/div #js {}
393 |       nil)))
394 | 
395 | (deftest test-nil-children
396 |   (is (= (str (#'dom/render-to-str* (nil-child-factory)))
397 |          "<!-- react-empty: 1 -->"))
398 |   (is (= (str (#'dom/render-to-str* ((om/factory NilChildrenComp))))
399 |          "<div data-reactroot=\"\" data-reactid=\"1\"></div>"))
400 |   (is (= (str (#'dom/render-to-str* ((om/factory NilParent))))
401 |          (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
402 |                                <!-- react-text: 2 -->foo<!-- /react-text -->
403 |                                <!-- react-empty: 3 -->
404 |                                <!-- react-text: 4 -->bar<!-- /react-text --></div>"))))
405 | 
406 | (defui CLPHN-3-Component-1
407 |   Object
408 |   (initLocalState [this]
409 |     {:a 1})
410 |   (componentWillMount [this]
411 |     (om/update-state! this update-in [:a] inc))
412 |   (render [this]
413 |     (dom/div nil (str "a: " (om/get-state this :a)))))
414 | 
415 | (defui CLPHN-3-Component-2
416 |   Object
417 |   (initLocalState [this]
418 |     {:a 1})
419 |   (componentWillMount [this]
420 |     (om/update-state! this update-in [:a] inc))
421 |   (render [this]
422 |     (dom/div nil (str "a: " (om/get-state this :a)))))
423 | 
424 | (defui CLPHN-3-Child
425 |   Object
426 |   (initLocalState [this]
427 |     {:a 1})
428 |   (componentWillMount [this]
429 |     (om/update-state! this update-in [:a] inc))
430 |   (render [this]
431 |     (dom/div nil (str "child a: " (om/get-state this :a)))))
432 | 
433 | (def clphn3-child (om/factory CLPHN-3-Child))
434 | 
435 | (defui CLPHN-3-Parent
436 |   Object
437 |   (initLocalState [this]
438 |     {:a 2})
439 |   (componentWillMount [this]
440 |     (om/update-state! this update-in [:a] inc))
441 |   (render [this]
442 |     (dom/div nil
443 |       (clphn3-child)
444 |       (str "parent a: " (om/get-state this :a)))))
445 | 
446 | (deftest test-clphn-3
447 |   (let [c1 ((om/factory CLPHN-3-Component-1))
448 |         c2 ((om/factory CLPHN-3-Component-2))
449 |         c3 ((om/factory CLPHN-3-Parent))]
450 |     (is (= (str (#'dom/render-to-str* c1))
451 |            "<div data-reactroot=\"\" data-reactid=\"1\">a: 2</div>"))
452 |     (is (= (str (#'dom/render-to-str* c2))
453 |            "<div data-reactroot=\"\" data-reactid=\"1\">a: 2</div>"))
454 |     (is (= (str (#'dom/render-to-str* c3))
455 |            (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
456 |                                  <div data-reactid=\"2\">child a: 2</div>
457 |                                  <!-- react-text: 3 -->parent a: 3<!-- /react-text -->
458 |                                </div>")))))
459 | 
460 | (defui SomeChild
461 |   Object
462 |   (render [this]
463 |     (dom/div nil "foo")))
464 | 
465 | (def some-child (om/factory SomeChild))
466 | 
467 | (defui SomeParent
468 |   Object
469 |   (render [this]
470 |     (some-child)))
471 | 
472 | (deftest test-om-644
473 |   (is (= (str (#'dom/render-to-str* ((om/factory SomeParent))))
474 |          "<div data-reactroot=\"\" data-reactid=\"1\">foo</div>")))
475 | 
476 | ;; React 15
477 | 
478 | (defui React15Comp
479 |   Object
480 |   (render [this]
481 |     (dom/div nil
482 |       (dom/div nil
483 |         "nested"
484 |         (dom/div nil "other")))))
485 | 
486 | (deftest react-15-render
487 |   (is (= (dom/render-to-str ((om/factory React15Comp)))
488 |          (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\" data-react-checksum=\"1635398171\">
489 |                                <div data-reactid=\"2\">
490 |                                  <!-- react-text: 3 -->nested<!-- /react-text -->
491 |                                  <div data-reactid=\"4\">other</div>
492 |                                </div>
493 |                              </div>")))
494 |   (is (= (dom/render-to-str
495 |            (dom/div nil
496 |              (dom/div nil
497 |                (dom/div nil "3"))
498 |              (dom/div nil
499 |                (dom/div nil "5"))))
500 |         (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\" data-react-checksum=\"-1239993276\">
501 |                               <div data-reactid=\"2\">
502 |                                 <div data-reactid=\"3\">3</div>
503 |                               </div>
504 |                               <div data-reactid=\"4\">
505 |                                 <div data-reactid=\"5\">5</div>
506 |                               </div>
507 |                             </div>"))))
508 | 
509 | (deftest render-wrapped-attrs
510 |   (is (= (str (#'dom/render-to-str* (dom/input #js {:value "foo" :id "bar" :type "text"})))
511 |          "<input type=\"text\" value=\"foo\" id=\"bar\" data-reactroot=\"\" data-reactid=\"1\"/>"))
512 |   (is (= (str (#'dom/render-to-str* (dom/option #js {:disabled "" :label "foo" :selected ""})))
513 |          "<option selected=\"\" disabled=\"\" label=\"foo\" data-reactroot=\"\" data-reactid=\"1\"></option>"))
514 |   ;; https://github.com/facebook/react/commit/fc0431
515 |   (is (= (str (#'dom/render-to-str* (dom/input #js {:value "foo" :step 3 :name "points" :type "number"})))
516 |          "<input type=\"number\" step=\"3\" value=\"foo\" name=\"points\" data-reactroot=\"\" data-reactid=\"1\"/>"))
517 |   (is (= (str (#'dom/render-to-str* (dom/input #js {:value "foo" :type "number" :name "points" :step 3})))
518 |          "<input type=\"number\" step=\"3\" value=\"foo\" name=\"points\" data-reactroot=\"\" data-reactid=\"1\"/>"))
519 |   ;; https://github.com/facebook/react/commit/3013af
520 |   (is (= (str (#'dom/render-to-str* (dom/input #js {:max 42 :value "foo" :min 10 :type "number" :name "points" :step 3})))
521 |          "<input type=\"number\" step=\"3\" min=\"10\" max=\"42\" value=\"foo\" name=\"points\" data-reactroot=\"\" data-reactid=\"1\"/>")))
522 | 
523 | (deftest test-om-800
524 |   (let [sb (StringBuilder.)]
525 |     (p/-render-to-string (dom/text-node "foo's") 0 sb)
526 |     (is (= (str sb) "foo&#x27;s")))
527 |   (let  [sb (StringBuilder.)]
528 |     (p/-render-to-string (dom/react-text-node "foo's") (volatile! 0) sb)
529 |     (is (= (str sb) (str "<!-- react-text: 0 -->foo&#x27;s<!-- /react-text -->")))))
530 | 
531 | (deftest test-om-818
532 |   (let  [sb (StringBuilder.)]
533 |     (p/-render-to-string (dom/script #js {:type "text/javascript"
534 |                                           :dangerouslySetInnerHTML {:__html "'foo bar'"}})
535 |       (volatile! 0) sb)
536 |     (is (= (str sb) "<script type=\"text/javascript\" data-reactid=\"0\">'foo bar'</script>"))))
537 | 
538 | (deftest test-om-799
539 |   (let [elem (dom/create-element "use")
540 |         sb (StringBuilder.)]
541 |     (is (instance? om.dom.Element elem))
542 |     (p/-render-to-string elem (volatile! 0) sb)
543 |     (is (= (str sb) "<use data-reactid=\"0\"></use>")))
544 |   (let [sb (StringBuilder.)]
545 |     (p/-render-to-string (dom/create-element "use" nil "use-child") (volatile! 0) sb)
546 |     (is (= (str sb) "<use data-reactid=\"0\">use-child</use>")))
547 |   (let [elem (dom/create-element "use"
548 |                #js {:key "foo"
549 |                     :x "50" :y "10"
550 |                     :xlinkHref "#Port"}
551 |                "use-child")
552 |         sb (StringBuilder.)]
553 |     (p/-render-to-string elem (volatile! 0) sb)
554 |     (is (= (str sb) "<use x=\"50\" y=\"10\" xlink:href=\"#Port\" data-reactid=\"0\">use-child</use>"))))
555 | 


--------------------------------------------------------------------------------
/src/test/om/next/run_tests.cljs:
--------------------------------------------------------------------------------
 1 | (ns om.next.run-tests
 2 |   (:require [cljs.test :refer-macros [run-tests]]
 3 |             [cljs.nodejs]
 4 |             [om.next.tests]))
 5 | 
 6 | (enable-console-print!)
 7 | 
 8 | (defn main []
 9 |   (run-tests 'om.next.tests))
10 | 
11 | (set! *main-cli-fn* main)


--------------------------------------------------------------------------------
/src/test/om/next/tutorials_test.clj:
--------------------------------------------------------------------------------
  1 | (ns om.next.tutorials-test
  2 |   (:refer-clojure :exclude [read])
  3 |   (:require [clojure.test :refer [deftest testing is are]]
  4 |             [om.test-utils :refer [remove-whitespace]]
  5 |             [om.next :as om :refer [defui]]
  6 |             [om.dom :as dom]))
  7 | 
  8 | ;; =============================================================================
  9 | ;; Quick Start
 10 | 
 11 | (def animals-app-state
 12 |   (atom
 13 |     {:app/title "Animals"
 14 |      :animals/list
 15 |      [[1 "Ant"] [2 "Antelope"] [3 "Bird"] [4 "Cat"] [5 "Dog"]
 16 |       [6 "Lion"] [7 "Mouse"] [8 "Monkey"] [9 "Snake"] [10 "Zebra"]]}))
 17 | 
 18 | (defmulti animals-read (fn [env key params] key))
 19 | 
 20 | (defmethod animals-read :default
 21 |   [{:keys [state] :as env} key params]
 22 |   (let [st @state]
 23 |     (if-let [[_ value] (find st key)]
 24 |       {:value value}
 25 |       {:value :not-found})))
 26 | 
 27 | (defmethod animals-read :animals/list
 28 |   [{:keys [state] :as env} key {:keys [start end]}]
 29 |   {:value (subvec (:animals/list @state) start end)})
 30 | 
 31 | (defui AnimalsList
 32 |   static om/IQueryParams
 33 |   (params [this]
 34 |     {:start 0 :end 10})
 35 |   static om/IQuery
 36 |   (query [this]
 37 |     '[:app/title (:animals/list {:start ?start :end ?end})])
 38 |   Object
 39 |   (render [this]
 40 |     (let [{:keys [app/title animals/list]} (om/props this)]
 41 |       (dom/div nil
 42 |         (dom/h2 nil title)
 43 |         (apply dom/ul nil
 44 |           (map
 45 |             (fn [[i name]]
 46 |               (dom/li nil (str i ". " name)))
 47 |             list))))))
 48 | 
 49 | (def animals-reconciler
 50 |   (om/reconciler
 51 |     {:state animals-app-state
 52 |      :parser (om/parser {:read animals-read})}))
 53 | 
 54 | (deftest test-render-animals-tutorial
 55 |   (let [result-markup (remove-whitespace
 56 |                         "<div data-reactroot=\"\" data-reactid=\"1\">
 57 |                            <h2 data-reactid=\"2\">Animals</h2>
 58 |                            <ul data-reactid=\"3\">
 59 |                              <li data-reactid=\"4\">1. Ant</li>
 60 |                              <li data-reactid=\"5\">2. Antelope</li>
 61 |                              <li data-reactid=\"6\">3. Bird</li>
 62 |                              <li data-reactid=\"7\">4. Cat</li>
 63 |                              <li data-reactid=\"8\">5. Dog</li>
 64 |                              <li data-reactid=\"9\">6. Lion</li>
 65 |                              <li data-reactid=\"10\">7. Mouse</li>
 66 |                              <li data-reactid=\"11\">8. Monkey</li>
 67 |                              <li data-reactid=\"12\">9. Snake</li>
 68 |                              <li data-reactid=\"13\">10. Zebra</li>
 69 |                            </ul>
 70 |                          </div>")]
 71 |     (testing "render with factory"
 72 |       (let [ctor (om/factory AnimalsList)]
 73 |         (is (= (str (#'dom/render-to-str* (ctor @animals-app-state))) result-markup))))
 74 |     (testing "render with reconciler & add-root!"
 75 |       (let [c (om/add-root! animals-reconciler AnimalsList nil)
 76 |             markup-str (str (#'dom/render-to-str* c))]
 77 |         (is (= (om/react-type (om/app-root animals-reconciler)) AnimalsList))
 78 |         (is (= markup-str result-markup))))))
 79 | 
 80 | ;; =============================================================================
 81 | ;; Om Links tutorial
 82 | 
 83 | (def links-init-data
 84 |   {:current-user {:email "bob.smith@gmail.com"}
 85 |    :items [{:id 0 :title "Foo"}
 86 |            {:id 1 :title "Bar"}
 87 |            {:id 2 :title "Baz"}]})
 88 | 
 89 | (defmulti links-read om/dispatch)
 90 | 
 91 | (defmethod links-read :items
 92 |   [{:keys [query state]} k _]
 93 |   (let [st @state]
 94 |     {:value (om/db->tree query (get st k) st)}))
 95 | 
 96 | (defui LinksItem
 97 |   static om/Ident
 98 |   (ident [_ {:keys [id]}]
 99 |     [:item/by-id id])
100 |   static om/IQuery
101 |   (query [_]
102 |     '[:id :title [:current-user _]])
103 |   Object
104 |   (render [this]
105 |     (let [{:keys [title current-user]} (om/props this)]
106 |       (dom/li nil
107 |         (dom/div nil title)
108 |         (dom/div nil (:email current-user))))))
109 | 
110 | (def links-item (om/factory LinksItem))
111 | 
112 | (defui LinksSomeList
113 |   static om/IQuery
114 |   (query [_]
115 |     [{:items (om/get-query LinksItem)}])
116 |   Object
117 |   (render [this]
118 |     (dom/div nil
119 |       (dom/h2 nil "A List!")
120 |       (dom/ul nil
121 |         (map links-item (-> this om/props :items))))))
122 | 
123 | (def links-reconciler
124 |   (om/reconciler
125 |     {:state links-init-data
126 |      :parser (om/parser {:read links-read})}))
127 | 
128 | (deftest test-render-links-tutorial
129 |   (let [c (om/add-root! links-reconciler LinksSomeList nil)]
130 |     (is (= (str (#'dom/render-to-str* c))
131 |            (remove-whitespace
132 |              "<div data-reactroot=\"\" data-reactid=\"1\">
133 |                 <h2 data-reactid=\"2\">A List!</h2>
134 |                 <ul data-reactid=\"3\">
135 |                   <li data-reactid=\"4\">
136 |                     <div data-reactid=\"5\">Foo</div>
137 |                     <div data-reactid=\"6\">bob.smith@gmail.com</div>
138 |                   </li>
139 |                   <li data-reactid=\"7\">
140 |                     <div data-reactid=\"8\">Bar</div>
141 |                     <div data-reactid=\"9\">bob.smith@gmail.com</div>
142 |                   </li>
143 |                   <li data-reactid=\"10\">
144 |                     <div data-reactid=\"11\">Baz</div>
145 |                     <div data-reactid=\"12\">bob.smith@gmail.com</div>
146 |                   </li>
147 |                 </ul>
148 |               </div>")))))
149 | 
150 | ;; =============================================================================
151 | ;; Componentes, Identity & Normalization
152 | 
153 | (def cian-init-data
154 |   {:list/one [{:name "John" :points 0}
155 |               {:name "Mary" :points 0}
156 |               {:name "Bob"  :points 0}]
157 |    :list/two [{:name "Mary" :points 0 :age 27}
158 |               {:name "Gwen" :points 0}
159 |               {:name "Jeff" :points 0}]})
160 | 
161 | ;; -----------------------------------------------------------------------------
162 | ;; Parsing
163 | 
164 | (defmulti cian-read om/dispatch)
165 | 
166 | (defn get-people [state key]
167 |   (let [st @state]
168 |     (into [] (map #(get-in st %)) (get st key))))
169 | 
170 | (defmethod cian-read :list/one
171 |   [{:keys [state] :as env} key params]
172 |   {:value (get-people state key)})
173 | 
174 | (defmethod cian-read :list/two
175 |   [{:keys [state] :as env} key params]
176 |   {:value (get-people state key)})
177 | 
178 | (defmulti cian-mutate om/dispatch)
179 | 
180 | (defmethod cian-mutate 'points/increment
181 |   [{:keys [state]} _ {:keys [name]}]
182 |   {:action
183 |    (fn []
184 |      (swap! state update-in
185 |        [:person/by-name name :points]
186 |        inc))})
187 | 
188 | (defmethod cian-mutate 'points/decrement
189 |   [{:keys [state]} _ {:keys [name]}]
190 |   {:action
191 |    (fn []
192 |      (swap! state update-in
193 |        [:person/by-name name :points]
194 |        #(let [n (dec %)] (if (neg? n) 0 n))))})
195 | 
196 | ;; -----------------------------------------------------------------------------
197 | ;; Components
198 | 
199 | (defui Person
200 |   static om/Ident
201 |   (ident [this {:keys [name]}]
202 |     [:person/by-name name])
203 |   static om/IQuery
204 |   (query [this]
205 |     '[:name :points :age])
206 |   Object
207 |   (render [this]
208 |     (println "Render Person" (-> this om/props :name))
209 |     (let [{:keys [points name foo] :as props} (om/props this)]
210 |       (dom/li nil
211 |         (dom/label nil (str name ", points: " points))
212 |         (dom/button
213 |           #js {:onClick
214 |                (fn [e]
215 |                  (om/transact! this
216 |                    `[(points/increment ~props)]))}
217 |           "+")
218 |         (dom/button
219 |           #js {:onClick
220 |                (fn [e]
221 |                  (om/transact! this
222 |                    `[(points/decrement ~props)]))}
223 |           "-")))))
224 | 
225 | (def person (om/factory Person {:keyfn :name}))
226 | 
227 | (defui ListView
228 |   Object
229 |   (render [this]
230 |     ;(println "Render ListView" (-> this om/path first))
231 |     (let [list (om/props this)]
232 |       (apply dom/ul nil
233 |         (map person list)))))
234 | 
235 | (def list-view (om/factory ListView))
236 | 
237 | (defui RootView
238 |   static om/IQuery
239 |   (query [this]
240 |     (let [subquery (om/get-query Person)]
241 |       `[{:list/one ~subquery} {:list/two ~subquery}]))
242 |   Object
243 |   (render [this]
244 |     (println "Render RootView")
245 |     (let [{:keys [list/one list/two]} (om/props this)]
246 |       (apply dom/div nil
247 |         [(dom/h2 nil "List A")
248 |          (list-view one)
249 |          (dom/h2 nil "List B")
250 |          (list-view two)]))))
251 | 
252 | (def cian-reconciler
253 |   (om/reconciler
254 |     {:state  cian-init-data
255 |      :parser (om/parser {:read cian-read :mutate cian-mutate})}))
256 | 
257 | (deftest test-cian-tutorial
258 |   (let [c (om/add-root! cian-reconciler RootView nil)]
259 |     (is (= (str (#'dom/render-to-str* c))
260 |            (remove-whitespace "<div data-reactroot=\"\" data-reactid=\"1\">
261 |                                  <h2 data-reactid=\"2\">List A</h2>
262 |                                  <ul data-reactid=\"3\">
263 |                                    <li data-reactid=\"4\">
264 |                                      <label data-reactid=\"5\">John, points: 0</label>
265 |                                      <button data-reactid=\"6\">+</button>
266 |                                      <button data-reactid=\"7\">-</button>
267 |                                    </li>
268 |                                    <li data-reactid=\"8\">
269 |                                      <label data-reactid=\"9\">Mary, points: 0</label>
270 |                                      <button data-reactid=\"10\">+</button>
271 |                                      <button data-reactid=\"11\">-</button>
272 |                                    </li>
273 |                                    <li data-reactid=\"12\">
274 |                                      <label data-reactid=\"13\">Bob, points: 0</label>
275 |                                      <button data-reactid=\"14\">+</button>
276 |                                      <button data-reactid=\"15\">-</button>
277 |                                    </li>
278 |                                  </ul>
279 |                                  <h2 data-reactid=\"16\">List B</h2>
280 |                                  <ul data-reactid=\"17\">
281 |                                    <li data-reactid=\"18\">
282 |                                      <label data-reactid=\"19\">Mary, points: 0</label>
283 |                                      <button data-reactid=\"20\">+</button>
284 |                                      <button data-reactid=\"21\">-</button>
285 |                                    </li>
286 |                                    <li data-reactid=\"22\">
287 |                                      <label data-reactid=\"23\">Gwen, points: 0</label>
288 |                                      <button data-reactid=\"24\">+</button>
289 |                                      <button data-reactid=\"25\">-</button>
290 |                                    </li>
291 |                                    <li data-reactid=\"26\">
292 |                                      <label data-reactid=\"27\">Jeff, points: 0</label>
293 |                                      <button data-reactid=\"28\">+</button>
294 |                                      <button data-reactid=\"29\">-</button>
295 |                                    </li>
296 |                                  </ul>
297 |                                </div>")))))
298 | 
299 | ;; =============================================================================
300 | ;; Queries with unions
301 | 
302 | (def union-init-data
303 |   {:dashboard/items
304 |    [{:id 0 :type :dashboard/post
305 |      :author "Laura Smith"
306 |      :title "A Post!"
307 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
308 |      :favorites 0}
309 |     {:id 1 :type :dashboard/photo
310 |      :title "A Photo!"
311 |      :image "photo.jpg"
312 |      :caption "Lorem ipsum"
313 |      :favorites 0}
314 |     {:id 2 :type :dashboard/post
315 |      :author "Jim Jacobs"
316 |      :title "Another Post!"
317 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
318 |      :favorites 0}
319 |     {:id 3 :type :dashboard/graphic
320 |      :title "Charts and Stufff!"
321 |      :image "chart.jpg"
322 |      :favorites 0}
323 |     {:id 4 :type :dashboard/post
324 |      :author "May Fields"
325 |      :title "Yet Another Post!"
326 |      :content "Lorem ipsum dolor sit amet, quem atomorum te quo"
327 |      :favorites 0}]})
328 | 
329 | (defui Post
330 |   static om/IQuery
331 |   (query [this]
332 |     [:id :type :title :author :content])
333 |   Object
334 |   (render [this]
335 |     (let [{:keys [title author content] :as props} (om/props this)]
336 |       (dom/div nil
337 |         (dom/h3 nil title)
338 |         (dom/h4 nil author)
339 |         (dom/p nil content)))))
340 | 
341 | (def post (om/factory Post))
342 | 
343 | (defui Photo
344 |   static om/IQuery
345 |   (query [this]
346 |     [:id :type :title :image :caption])
347 |   Object
348 |   (render [this]
349 |     (let [{:keys [title image caption]} (om/props this)]
350 |       (dom/div nil
351 |         (dom/h3 nil (str "Photo: " title))
352 |         (dom/div nil image)
353 |         (dom/p nil "Caption: ")))))
354 | 
355 | (def photo (om/factory Photo))
356 | 
357 | (defui Graphic
358 |   static om/IQuery
359 |   (query [this]
360 |     [:id :type :title :image])
361 |   Object
362 |   (render [this]
363 |     (let [{:keys [title image]} (om/props this)]
364 |       (dom/div nil
365 |         (dom/h3 nil (str "Graphic: " title))
366 |         (dom/div nil image)))))
367 | 
368 | (def graphic (om/factory Graphic))
369 | 
370 | (defui DashboardItem
371 |   static om/Ident
372 |   (ident [this {:keys [id type]}]
373 |     [type id])
374 |   static om/IQuery
375 |   (query [this]
376 |     (zipmap
377 |       [:dashboard/post :dashboard/photo :dashboard/graphic]
378 |       (map #(conj % :favorites)
379 |         [(om/get-query Post)
380 |          (om/get-query Photo)
381 |          (om/get-query Graphic)])))
382 |   Object
383 |   (render [this]
384 |     (let [{:keys [id type favorites] :as props} (om/props this)]
385 |       (dom/li
386 |         #js {:style #js {:padding 10 :borderBottom "1px solid black"}}
387 |         (dom/div nil
388 |           (({:dashboard/post    post
389 |              :dashboard/photo   photo
390 |              :dashboard/graphic graphic} type)
391 |             (om/props this)))
392 |         (dom/div nil
393 |           (dom/p nil (str "Favorites: " favorites))
394 |           (dom/button
395 |             #js {:onClick
396 |                  (fn [e]
397 |                    (om/transact! this
398 |                      `[(dashboard/favorite {:ref [~type ~id]})]))}
399 |             "Favorite!"))))))
400 | 
401 | (def dashboard-item (om/factory DashboardItem))
402 | 
403 | (defui Dashboard
404 |   static om/IQuery
405 |   (query [this]
406 |     [{:dashboard/items (om/get-query DashboardItem)}])
407 |   Object
408 |   (render [this]
409 |     (let [{:keys [dashboard/items]} (om/props this)]
410 |       (apply dom/ul
411 |         #js {:style #js {:padding 0}}
412 |         (map dashboard-item items)))))
413 | 
414 | (defmulti union-read om/dispatch)
415 | 
416 | (defmethod union-read :dashboard/items
417 |   [{:keys [state]} k _]
418 |   (let [st @state]
419 |     {:value (into [] (map #(get-in st %)) (get st k))}))
420 | 
421 | (defmulti mutate om/dispatch)
422 | 
423 | (defmethod mutate 'dashboard/favorite
424 |   [{:keys [state]} k {:keys [ref]}]
425 |   {:action
426 |    (fn []
427 |      (swap! state update-in (conj ref :favorites) inc))})
428 | 
429 | (def union-reconciler
430 |   (om/reconciler
431 |     {:state  union-init-data
432 |      :parser (om/parser {:read union-read :mutate mutate})}))
433 | 
434 | 
435 | (deftest test-unions-tutorial
436 |     (let [c (om/add-root! union-reconciler Dashboard nil)]
437 |       (is (= (str (#'dom/render-to-str* c))
438 |              (remove-whitespace
439 |                "<ul style=\"padding:0;\" data-reactroot=\"\" data-reactid=\"1\">
440 |                  <li style=\"padding:10px;border-bottom:1px solid black;\" data-reactid=\"2\">
441 |                    <div data-reactid=\"3\">
442 |                      <div data-reactid=\"4\">
443 |                        <h3 data-reactid=\"5\">A Post!</h3>
444 |                        <h4 data-reactid=\"6\">Laura Smith</h4>
445 |                        <p data-reactid=\"7\">Lorem ipsum dolor sit amet, quem atomorum te quo</p>
446 |                      </div>
447 |                    </div>
448 |                    <div data-reactid=\"8\">
449 |                      <p data-reactid=\"9\">Favorites: 0</p>
450 |                      <button data-reactid=\"10\">Favorite!</button>
451 |                    </div>
452 |                  </li>
453 |                  <li style=\"padding:10px;border-bottom:1px solid black;\" data-reactid=\"11\">
454 |                    <div data-reactid=\"12\">
455 |                      <div data-reactid=\"13\">
456 |                        <h3 data-reactid=\"14\">Photo: A Photo!</h3>
457 |                        <div data-reactid=\"15\">photo.jpg</div>
458 |                        <p data-reactid=\"16\">Caption: </p>
459 |                      </div>
460 |                    </div>
461 |                    <div data-reactid=\"17\">
462 |                      <p data-reactid=\"18\">Favorites: 0</p>
463 |                      <button data-reactid=\"19\">Favorite!</button>
464 |                    </div>
465 |                  </li>
466 |                  <li style=\"padding:10px;border-bottom:1px solid black;\" data-reactid=\"20\">
467 |                    <div data-reactid=\"21\">
468 |                      <div data-reactid=\"22\">
469 |                        <h3 data-reactid=\"23\">Another Post!</h3>
470 |                        <h4 data-reactid=\"24\">Jim Jacobs</h4>
471 |                        <p data-reactid=\"25\">Lorem ipsum dolor sit amet, quem atomorum te quo</p>
472 |                      </div>
473 |                    </div>
474 |                    <div data-reactid=\"26\">
475 |                      <p data-reactid=\"27\">Favorites: 0</p>
476 |                      <button data-reactid=\"28\">Favorite!</button>
477 |                    </div>
478 |                  </li>
479 |                  <li style=\"padding:10px;border-bottom:1px solid black;\" data-reactid=\"29\">
480 |                    <div data-reactid=\"30\">
481 |                      <div data-reactid=\"31\">
482 |                        <h3 data-reactid=\"32\">Graphic: Charts and Stufff!</h3>
483 |                        <div data-reactid=\"33\">chart.jpg</div>
484 |                      </div>
485 |                    </div>
486 |                    <div data-reactid=\"34\">
487 |                      <p data-reactid=\"35\">Favorites: 0</p>
488 |                      <button data-reactid=\"36\">Favorite!</button>
489 |                    </div>
490 |                  </li>
491 |                  <li style=\"padding:10px;border-bottom:1px solid black;\" data-reactid=\"37\">
492 |                    <div data-reactid=\"38\">
493 |                      <div data-reactid=\"39\">
494 |                        <h3 data-reactid=\"40\">Yet Another Post!</h3>
495 |                        <h4 data-reactid=\"41\">May Fields</h4>
496 |                        <p data-reactid=\"42\">Lorem ipsum dolor sit amet, quem atomorum te quo</p>
497 |                      </div>
498 |                    </div>
499 |                    <div data-reactid=\"43\">
500 |                      <p data-reactid=\"44\">Favorites: 0</p>
501 |                      <button data-reactid=\"45\">Favorite!</button>
502 |                    </div>
503 |                  </li>
504 |                </ul>")))))
505 | 


--------------------------------------------------------------------------------
/src/test/om/test_utils.clj:
--------------------------------------------------------------------------------
1 | (ns om.test-utils
2 |   (:require [clojure.string :as str]))
3 | 
4 | (defn remove-whitespace [s]
5 |   (str/replace s #"(>)\s+(<)" "$1$2"))
6 | 


--------------------------------------------------------------------------------
/src/test/om/tests.cljs:
--------------------------------------------------------------------------------
 1 | (ns om.tests
 2 |   (:require [cljs.test :refer-macros [is are deftest run-tests]]
 3 |             [om.core :as om :include-macros true]
 4 |             [om.dom :as dom :include-macros true]))
 5 | 
 6 | (enable-console-print!)
 7 | 
 8 | (defprotocol IFoo
 9 |   (-foo [this]))
10 | 
11 | (defn derive* [cursor]
12 |   (specify cursor
13 |     om/ICursorDerive
14 |     (-derive [this derived state path]
15 |       (derive* (om/to-cursor derived state path)))
16 |     IFoo
17 |     (-foo [_] :foo)))
18 | 
19 | (deftest cursor-protocols 
20 |   (is (om/cursor? (om/to-cursor [1 2 3])))
21 |   (is (om/cursor? (om/to-cursor {:foo "bar"})))
22 |   (are [x y] (= x y)
23 |        (.-value (om/to-cursor [1 2 3])) [1 2 3]
24 |        (.-value (om/to-cursor {:foo "bar"})) {:foo "bar"}
25 |        (first (om/to-cursor [1 2 3])) 1
26 |        (first (om/to-cursor {:foo "bar"})) [:foo "bar"]
27 |        (:foo (first (om/to-cursor [{:foo "bar"}]))) "bar"
28 |        (.-path (first (om/to-cursor [{:foo "bar"}]))) [0]
29 |        (.-path (first (rest (om/to-cursor [{:foo "bar"} {:baz "woz"}])))) [1]
30 |        (rest (rest (om/to-cursor [{:foo "bar"} {:baz "woz"}]))) ()
31 |        (.-path (first (next (om/to-cursor [{:foo "bar"} {:baz "woz"}])))) [1]
32 |        (.-path (get-in (om/to-cursor {:foo [{:id 1}]}) [:foo 0])) [:foo 0]
33 |        (get-in (om/to-cursor {:foo [{:id 1}]}) [:foo 0 :id]) 1
34 |        (assoc (om/to-cursor {:foo 1}) :bar 2) {:foo 1 :bar 2}
35 |        {:foo 1 :bar 2} (assoc (om/to-cursor {:foo 1}) :bar 2)
36 |        (dissoc (om/to-cursor {:foo 1}) :foo) {}
37 |        {} (dissoc (om/to-cursor {:foo 1}) :foo)
38 |        (map identity (om/to-cursor [{:id 1} {:id 2} {:id 3}]))
39 |        [{:id 1} {:id 2} {:id 3}]
40 |        [{:id 1} {:id 2} {:id 3}]
41 |        (map identity (om/to-cursor [{:id 1} {:id 2} {:id 3}])))
42 |   (let [c (derive* (om/to-cursor {:foo {:bar {:baz 'woz}}} [] nil))]
43 |     (is (= (-foo (get-in c [:foo :bar])) :foo))))
44 | 
45 | (run-tests)
46 | 


--------------------------------------------------------------------------------