refine alert rendering

refine alert rendering

--- src/clj_money/web/shared.clj
+++ src/clj_money/web/shared.clj
@@ -45,10 +45,10 @@
   "Renders notifications as HTML"
   [alerts]
   (map (fn [alert]
-         (let [css-class (if (string? alert)
-                           :info
-                           (:type alert))]
-           [:div.alert {:class css-class}]))
+         (let [[css-class message] (if (string? alert)
+                           [:info alert]
+                           [(:type alert) (:message alert)])]
+           [:div {:class (str "alert alert-" (name css-class))} message]))
        alerts))

 (defn layout


add support for

add support for

--- src/cljs/uberblick/core.cljs
+++ src/cljs/uberblick/core.cljs
@@ -63,7 +63,23 @@
       (println "Error while evaluating string")
       (prn e))))

-;; Todo: Add ":my-bla"
+(defn replace-current-user-symbols
+  "replaces keywords that are given in the form $keyword to the value of lookups to the given profile, e.g.
+  the string \"(= :UserID $UserID)\" with the profile including {:UserID 123} will result in the string \"(= :UserID 123)\" "
+  [profile expr]
+  (let [$->kw (fn [kw] (->> kw str rest (apply str) keyword ))
+        $? (fn [kw] (= \$ (first (str kw))))
+        transform$ (fn [a] (if ($? a)
+                            (get profile ($->kw a))
+                            a))]
+    (try
+      (some->> expr
+               (read-string)
+               (walk/postwalk transform$)
+               str)
+      (catch :default e
+        (prn 'replace-current-user-symbols e)))))
+
 (defn create-filter-fn
   "Creates a filter function of the form:
   (fn [user] expr-as-fn)
@@ -78,7 +94,6 @@
   "
   [thekeys expr]
   (let [my-user (gensym "user")
-        un-myify (fn [kw] (-> kw str (.replace ":-my" "") keyword))
         replace-where-appropriate (fn [sym]
                                     (cond
                                       (not (keyword? sym)) sym
@@ -124,8 +139,13 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; filter functions
 (defn add-filter
-  [filter-str]
-  (swap! filters conj filter-str))
+  [profile filter-str]
+  (let [transformed-filter (replace-current-user-symbols profile filter-str)]
+    (if transformed-filter
+      (do
+        (swap! filters conj transformed-filter)
+        transformed-filter)
+      nil)))

 (defn vec-remove
   "remove elem in coll"
@@ -200,26 +220,25 @@
   (with-out-str (cljs.pprint/pprint data)))

 (defn Instructions
-  []
-  (let [current-profile (atom "Nothing here")]
-    (go
-      (let [profile (<! (genome/<current-user-profile))]
-        (reset! current-profile (-> profile prettify ))))
-    (fn []
-      [:div
-       [:p "Here is a list of all available attributes:"
-        [:p (str all-profile-keys)]
-        "And for the record, here is your profile:"]
-       [:pre @current-profile]])))
-
-(defn New-Filter-Field [inputatm]
+  [current-profile]
+  [:div
+   [:p "Here is a list of all available attributes:"
+    [:p (str all-profile-keys)]
+    "And for the record, here is your profile:"]
+   [:pre @current-profile]])
+
+(defn New-Filter-Field [current-profile inputatm]
   [:div.row
    [:input.col.s9.offset-s1 {:type "text"
                              :placeholder "Add a new filter"
                              :value @inputatm
                              :on-change #(reset! inputatm (-> % .-target .-value))}]
-   [:button.col.s1.btn {:on-click #(do (add-filter @inputatm)
-                                       (reset! inputatm ""))} "Add"]])
+   [:button.col.s1.btn {:on-click #(do
+                                     (let [success (add-filter @current-profile @inputatm)]
+                                       (if success
+                                         (reset! inputatm "")
+                                         (js/alert "There has been a problem with your filter!")))
+                                     )} "Add"]])

 (defn Current-Filters [inputatm]
   [:ul.collection
@@ -235,32 +254,39 @@
                     [:a {:on-click #(remove-filter idx)} [:i.material-icons "delete"]]
                     ]]) @filters)])

-(defn Examples [filter-examples]
+(defn Examples [current-profile filter-examples]
   [:ul.collection
    (map-indexed (fn [idx filter]
                   ^{:key filter}
-                  [:li.collection-item filter [:a.secondary-content {:on-click #(add-filter filter)} [:i.material-icons "playlist_add"]]]) filter-examples)])
+                  [:li.collection-item filter [:a.secondary-content {:on-click #(add-filter @current-profile filter)} [:i.material-icons "playlist_add"]]]) filter-examples)])

 (defn Filters []
-  (let [inputatm (atom "")]
+  (let [inputatm (atom "")
+        current-profile (atom nil)
+        current-profile-str (atom "")]
+    (go
+      (let [profile (reset! current-profile (<! (genome/<current-user-profile)))]
+        (reset! current-profile-str (-> profile prettify ))))
     (fn []
       [:div
        [:h3 "Filters"]
        [Current-Filters inputatm]
-       [New-Filter-Field inputatm]
+       [New-Filter-Field current-profile inputatm]
        [:hr]
        [:h3 "Examples"]
        [:p "Here are some example filters to get you started!"]
-       [Examples [
-                  "(startsWith? :FirstName \"M\")"
-                  "(= :LaborRoleID \"APPLDEVL\")"
-                  "(substring? :Title \"Quality\")"
-                  "(= :KeyscanStatus \"In 4th Flr N\")"
-                  "(or (startsWith? :FirstName \"M\") (substring? :Title \"Edit\"))"
+       [Examples current-profile [
+                                  "(startsWith? :FirstName \"M\")"
+                                  "(= :LaborRoleID \"APPLDEVL\")"
+                                  "(= :WorkTeamID $WorkTeamID)"
+                                  "(substring? :Title \"Quality\")"
+                                  "(>= :UserID $UserID)"
+                                  "(= :KeyscanStatus \"In 4th Flr N\")"
+                                  "(or (startsWith? :FirstName \"M\") (substring? :Title \"Edit\"))"
                   ]]
        [:hr]
        [:h3 "References"]
-       [Instructions]])))
+       [Instructions current-profile-str]])))

 (defn current-page []
   [:div [(session/get :current-page)]])


Make lattice-editor show errors with force layout

Signed-off-by: Daniel Borchmann <25857343a15bf1edafebc2912e82ecf775c585c1@mailbox.tu-dresden.de>

--- src/conexp/graphics/draw.clj
+++ src/conexp/graphics/draw.clj
@@ -67,6 +67,12 @@
 				frame,
 				"Invalid number in parameters.",
 				"Invalid parameters.",
+				JOptionPane/ERROR_MESSAGE))
+			     (catch Exception e
+			       (JOptionPane/showMessageDialog
+				frame,
+				(.getMessage e)
+				"An Error occured."
 				JOptionPane/ERROR_MESSAGE))))))))

 ;; TODO: Add energy label


Fixing wrong type hinting

Fixing wrong type hinting

Signed-off-by: Daniel Borchmann <25857343a15bf1edafebc2912e82ecf775c585c1@tu-dresden.de>

--- src/conexp/layouts/force.clj
+++ src/conexp/layouts/force.clj
@@ -17,7 +17,7 @@

 ;; Helpers

-(defn- ^double square [^double x]
+(defn- square ^double [^double x]
   (* x x))

 (defn- line-length-squared


fix #315: lookup ref should not throw in pull

See https://github.com/tonsky/datascript/issues/315

A pull ref lookup failure should result in nil rather than an error.

--- src/datascript/pull_api.cljc
+++ src/datascript/pull_api.cljc
@@ -266,7 +266,7 @@

 (defn pull-spec
   [db pattern eids multi?]
-  (let [eids (into [] (map #(db/entid-strict db %)) eids)]
+  (let [eids (into [] (map #(db/entid db %)) eids)]
     (pull-pattern db (list (initial-frame pattern eids multi?)))))

 (defn pull [db selector eid]


Problem added: Simple Recursion

Problem added: Simple Recursion

--- src/foreclojure/data_set.clj
+++ src/foreclojure/data_set.clj
@@ -571,4 +571,23 @@
             :tags ["medium" "seqs" "core-functions"]
             :tests ["(= (__ [1 2 1 3 1 2 4]) [1 2 3 4])"
                     "(= (__ [:a :a :b :b :c :c]) [:a :b :c])"
-                    "(= (__ '([2 4] [1 2] [1 3] [1 3])) '([2 4] [1 2] [1 3]))"]})))
+                    "(= (__ '([2 4] [1 2] [1 3] [1 3])) '([2 4] [1 2] [1 3]))"]})
+
+      (insert! :problems
+           {:_id 56
+            :title "Find Distinct Items"
+            :times-solved 0
+            :restricted ["distinct"]
+            :description "Write a function which removes the duplicates from a sequence. Order of the items must be maintained."
+            :tags ["medium" "seqs" "core-functions"]
+            :tests ["(= (__ [1 2 1 3 1 2 4]) [1 2 3 4])"
+                    "(= (__ [:a :a :b :b :c :c]) [:a :b :c])"
+                    "(= (__ '([2 4] [1 2] [1 3] [1 3])) '([2 4] [1 2] [1 3]))"]})
+
+      (insert! :problems
+           {:_id 57
+            :title "Simple Recursion"
+            :times-solved 0
+            :description "A recursive function is a function which calls itself.  This is one of the fundamental techniques used in functional programming."
+            :tags ["elementary" "recursion"]
+            :tests ["(= __ ((fn foo [x] (when (> x 0) (conj (foo (dec x)) x))) 5))"]})))


Changed "Analyze Tic-Tac-Toe Board" to return nil if no one has won

--- src/foreclojure/data_set.clj
+++ src/foreclojure/data_set.clj
@@ -756,15 +756,15 @@
           {:_id 73
            :title "Analyze a Tic-Tac-Toe Board"
            :times-solved 0
-           :description "A tic-tac-toe board is represented by a two dimensional vector. X is represented by :x, O is represented by :o, and empty is represented by :e.  A player wins by placing three Xs or three Os in a horizontal, vertical, or diagonal row.  Write a function which analyzes a tic-tac-toe board and returns :x if X has won, :o if O has won, and :e if neither player has won."
+           :description "A tic-tac-toe board is represented by a two dimensional vector. X is represented by :x, O is represented by :o, and empty is represented by :e.  A player wins by placing three Xs or three Os in a horizontal, vertical, or diagonal row.  Write a function which analyzes a tic-tac-toe board and returns :x if X has won, :o if O has won, and nil if neither player has won."
            :tags ["medium" "game"]
-           :tests ["(= :e (__ [[:e :e :e]\n           [:e :e :e]\n           [:e :e :e]]))"
+           :tests ["(= nil (__ [[:e :e :e]\n            [:e :e :e]\n            [:e :e :e]]))"
 		   "(= :x (__ [[:x :e :o]\n           [:x :e :e]\n           [:x :e :o]]))"
 		   "(= :o (__ [[:e :x :e]\n           [:o :o :o]\n           [:x :e :x]]))"
-		   "(= :e (__ [[:x :e :o]\n           [:x :x :e]\n           [:o :x :o]]))"
+		   "(= nil (__ [[:x :e :o]\n            [:x :x :e]\n            [:o :x :o]]))"
 		   "(= :x (__ [[:x :e :e]\n           [:o :x :e]\n           [:o :e :x]]))"
 		   "(= :o (__ [[:x :e :o]\n           [:x :o :e]\n           [:o :e :x]]))"
-		   "(= :e (__ [[:x :o :x]\n           [:x :o :x]\n           [:o :x :o]]))"]})
+		   "(= nil (__ [[:x :o :x]\n            [:x :o :x]\n            [:o :x :o]]))"]})

       (insert! :problems
            {:_id 74


Added instance? to the restricted list for "Black Box Testing"

--- src/foreclojure/data_set.clj
+++ src/foreclojure/data_set.clj
@@ -682,7 +682,7 @@
                     "(= :set (__ #{10 (rand-int 5)}))",
                     "(= [:map :set :vector :list] (map __ [{} #{} [] ()]))"]
             :restricted (map str '[class type Class vector? sequential?
-                                   list? seq? map? set? getClass])})
+                                   list? seq? map? set? instance? getClass])})

       (insert! :problems
           {:_id 66


Fixed bad url in 'accepted' email

Fixed bad url in 'accepted' email

--- src/foreclojure/problems.clj
+++ src/foreclojure/problems.clj
@@ -545,7 +545,7 @@
          {:from "team@4clojure.com"
           :to [(users/email-address user)]
           :subject (format "Problem #%d: submission accepted" id)
-          :html (html (link-to url title))
+          :html (html (link-to (str "http://www.4clojure.com/" url) title))
           :text (str title ": " url)})
         ;; TODO: dump this in a proper log
         (catch EmailException e (println (str "email failed to send on approved problem #" id))))


Changed textarea instructions to something more descriptive of what needs to be done

--- src/foreclojure/problems.clj
+++ src/foreclojure/problems.clj
@@ -104,7 +104,7 @@
          [:u "Special Restrictions"] [:br]
          (map (partial vector :li) restricted)])]
      [:div
-      [:b "Enter your code:" [:br]
+      [:b "Code which fills in the blank:" [:br]
        [:span {:class "error"} (session/flash-get :error)]]]
      (form-to [:post "/run-code"]
               (text-area {:id "code-box"


style the login page

style the login page

--- src/greybear/pages/login.clj
+++ src/greybear/pages/login.clj
@@ -20,13 +20,42 @@
         [greybear.pages.layout :only [base-layout]]))

 (defn login-get [request login_failed]
-  (base-layout "Login" request
-               (if (= login_failed "Y")
-                 [:div.alert.alert-danger {:role "alert"}
-                  "Login failed. "
-                  "The username or password you provided are incorrect."])
-               [:div#login
-                (form-to [:post "/login"]
-                         [:div#username (text-field "username")]
-                         [:div#password (password-field "password")]
-                         (submit-button "login"))]))
+  (base-layout
+   "Login" request
+   [:div.row
+    [:div.col-md-4]
+    [:div.col-md-4
+     [:div.panel.panel-default
+      [:div.panel-heading "Login"]
+      [:div.panel-body
+       (if (= login_failed "Y")
+         [:div.alert.alert-danger {:role "alert"}
+          "Login failed. "
+          "The username or password you provided are incorrect."])
+
+       [:div#login
+        (form-to
+         [:post "/login"]
+         [:div.row
+          [:div.col-md-2]
+          [:div.col-md-8
+           [:div.form-group
+            (label "input-username" "Username:")
+            (text-field {:id "input-username"
+                         :class "form-control"
+                         :placeholder "username"}
+                        "username")]
+           [:div.form-group
+            (label "input-password" "Password:")
+            (password-field {:id "input-password"
+                             :class "form-control"
+                             :placeholder "password"}
+                            "password")]]
+          [:div.col-md-2]]
+         [:div.row
+          [:div.col-md-4]
+          [:div.col-md-4
+           [:div.form-group (submit-button {:class "btn btn-primary"}
+                                           "Login")]]
+          [:div.col-md-4]])]]]]
+    [:div.col-md-4]]))


Hardcode a URL

Hardcode a URL

--- src/icecap/handlers/http.clj
+++ src/icecap/handlers/http.clj
@@ -8,4 +8,5 @@
 (defstep :http
   {:url URI}
   [step]
-  (connect (request step) (chan)))
+  (let [ch (chan)]
+    (connect (request {:url "http://www.rackspace.com"}) ch)))


resolve conflict

resolve conflict

--- bake/bake/test.clj
+++ bake/bake/test.clj
@@ -43,15 +43,7 @@
   (test/with-test-out
     (print-results m)))

-<<<<<<< HEAD
-(defmethod clojure.test/report :fn-summary [m]
-  (clojure.test/with-test-out
-    (print-results m)))
-
-(defmethod clojure.test/report :begin-auto [m])
-=======
 (defmethod test/report :begin-auto [m])
->>>>>>> fix bugs in test

 (defmethod test/report :summary-auto [m]
   (test/with-test-out


fix invoke tweak bug for IIIK

fix invoke tweak bug for IIIK

immediately-invoked-inferred-keyword ((if foo :foo :bar))

was treating it as a keyword too early and trying to get
the name of the list resulting in ClassCastException

apparently that case isn't covered in the tests

--- src/main/shadow/build/cljs_hacks.cljc
+++ src/main/shadow/build/cljs_hacks.cljc
@@ -779,12 +779,12 @@
             (ana/warning :fn-arity env {:name f :argc argc}))
           ;; FIXME: check first arg for defrecord field access opt
           (let [arg-expr (first @args-exprs)
-                arg-tag (ana/infer-tag enve arg-expr)
-                field-sym (symbol (name f))]
+                arg-tag (ana/infer-tag enve arg-expr)]

             (if (and (and (keyword? f) (nil? (namespace f)))
                      (== 1 argc)
-                     (record-with-field? arg-tag field-sym))
+                     (let [field-sym (symbol (name f))]
+                       (record-with-field? arg-tag field-sym)))
               ;; emit optimized (. thing -foo) for (:foo thing) if thing is a known record with foo field
               (ana/analyze env
                 (with-meta


day 17 more macros

day 17 more macros

--- src/adventofcode/day17.clj
+++ src/adventofcode/day17.clj
@@ -24,15 +24,18 @@
        (foor bindings# (vec symbols#)))))

 (defmacro combinations-3 [sizes liters]
-  (let [symbols# (map #(symbol (str "n" %)) sizes)
-        ranges# (map #(list range 0 (inc (quot liters %))) sizes)
-        bindings# (-> (vec (interleave symbols# ranges#))
-                      (conj :when (list '= liters
-                                        (list 'apply '+ (conj symbols# 'list)))))]
-    `(for ~bindings# [~@symbols#])))
+  `(for
+    [~@(concat (interleave
+                (map #(symbol (str "n" %)) sizes)
+                (map #(list range 0 (inc (quot liters %))) sizes))
+               (list :when (list '= liters
+                                 (list 'apply '+ (conj (map #(symbol (str "n" %)) sizes) 'list)))))]
+     [~@(map #(symbol (str "n" %)) sizes)]))

-(defmacro foor [& forms]
-  `(for ~@forms))
+(defmacro for' [bindings body]
+  (let [bindings# bindings
+        body# body]
+    `(for [~@bindings] [~@body])))

 (foor [n (range 2)] n)

@@ -42,10 +45,22 @@
 (macroexpand-1 '(combinations-2 test-vec 10))

 (combinations-3 test-vec 10)
-(macroexpand-1 '(combinations-3 test-vec 10))
+(clojure.pprint/pprint (macroexpand-1 '(combinations-3 test-vec 10)))

 (def test-vec [1 2])
 (combinations test-vec 10)
+
+(defn gen-bind-body [sizes liters]
+  (let [symbols (map #(gensym (str "n" %)) sizes)
+        ranges (map #(list 'range 0 (inc (quot liters %))) sizes)
+        bindings (concat (interleave symbols ranges)
+                         (list :when (list '= liters
+                                           (list 'apply '+ (conj symbols 'list)))))]
+    [bindings symbols]))
+
+(clojure.pprint/pprint (let [[binds body] (gen-bind-body (list 1 2) 10)]
+                         (eval (list 'for' binds body))))
+(eval (deref test-vec))

 (def containers (->> "day17.txt"
                      io/resource
@@ -53,5 +68,6 @@
                      slurp
                      str/split-lines
                      (mapv read-string)))
-
-(def part-one (combinations containers 150))
+(require '[clojure.pprint :refer [pprint]])
+(def part-one (let [[binds body] (pprint (gen-bind-body containers 150))]
+                (count (eval (list 'for' binds body)))))


Use single pod for print-meta

Use single pod for print-meta

--- src/io/perun.clj
+++ src/io/perun.clj
@@ -29,15 +29,16 @@
 (def ^:private print-meta-deps
   '[[mvxcvi/puget "1.0.0"]])

+(def print-meta-pod (delay (create-pod' print-meta-deps)))
+
 (deftask print-meta
   "Utility task to print perun metadata"
   [m map-fn MAPFN code "function to map over metadata items before printing"]
-  (let [pod (create-pod print-meta-deps)]
-    (boot/with-pre-wrap fileset
-      (let [map-fn (or map-fn identity)]
-        (pod/with-call-in @pod
-         (io.perun.print-meta/print-meta ~(vec (map map-fn (perun/get-meta fileset))))))
-      fileset)))
+  (boot/with-pre-wrap fileset
+    (let [map-fn (or map-fn identity)]
+      (pod/with-call-in @print-meta-pod
+        (io.perun.print-meta/print-meta ~(vec (map map-fn (perun/get-meta fileset))))))
+    fileset))

 (def ^:private filedata-deps
   '[[com.novemberain/pantomime "2.8.0"]])


bump lein-vanity to 0.2.0

bump lein-vanity to 0.2.0

--- dot/lein/profiles.clj
+++ dot/lein/profiles.clj
@@ -1,5 +1,5 @@
 {:user
- {:plugins [[lein-vanity "0.1.0"]
+ {:plugins [[lein-vanity "0.2.0"]
             [jonase/eastwood "0.0.2"]
             ;[lein-ritz "0.7.0"]
             [com.palletops/pallet-lein "0.8.0-alpha.1"]]


Add header "search" to search page.

Add header "search" to search page.

--- src/beebster_clj/views.clj
+++ src/beebster_clj/views.clj
@@ -47,10 +47,12 @@
   (html
    (base-template "Search")
    (header '(("/about" "about")))
+   [:h2.header "Search"]
    [:div.sform
     [:form {:method "post" :role "form" :action "/results"}
      [:div.form-group
-      [:input.form-control {:type "text" :name "searchvalue"}]]
+      [:p "Enter Search term:"]
+      [:input.form-control {:type "text" :name "searchvalue" }]]
      [:div.form-group
       [:input {:type "submit" :value "Search" :class "btn btn-default"}]]]]))

@@ -130,15 +132,16 @@
       [:p (first title)]]
      [:div.infothumb
       [:img {:src (first thumb)}]]
-     [:form.form-inline
-      {:role "form" :method "post" :action (apply str "/download?index=" index)}
-      [:div.form-group
-       [:select {:name "mode" :label.sr-only "download modes"}
-        (for [i modes]
-          [:option {:value i :selected (= i "mode") } i])]]
-      [:div.form-group
-       [:input {:type "submit" :value "Download" :class "btn btn-default"
-                :label.sr-only "download-modes"}]]]
+     [:div.infoform
+      [:form.form-inline
+       {:role "form" :method "post" :action (apply str "/download?index=" index)}
+       [:div.form-group
+        [:select {:name "mode" :label.sr-only "download modes"}
+         (for [i modes]
+           [:option {:value i :selected (= i "mode") } i])]]
+       [:div.form-group
+        [:input {:type "submit" :value "Download" :class "btn btn-default"
+                 :label.sr-only "download-modes"}]]]]
      [:div.iplayerinfo
       [:p (first desc)]])))


fixed matrix-mult for v38

fixed matrix-mult for v38

--- src/brevis/math/matrix.clj
+++ src/brevis/math/matrix.clj
@@ -23,7 +23,7 @@
 (defn matrix-mult
   "Matrix multiply"
   [^BasicMatrix m1 ^BasicMatrix m2]
-  (.multiplyRight m1 m2)); this needs to change to .multiply when switching to v38
+  (.multiply m1 m2)); switched from .multiplyRight when switching to v38

 (defn elmult
   "Matrix multiply by elements"


Don't break a page after a marker

Don't break a page after a marker

--- src/catalog/layout/fop.clj
+++ src/catalog/layout/fop.clj
@@ -58,7 +58,7 @@
                          :retrieve-boundary "page-sequence"}]])

 (defn- set-marker [title]
-  [:fo:block
+  [:fo:block {:keep-with-next "always"}
    [:fo:marker {:marker-class-name running-header-class-name} title]])

 (defn toc-entry [items fmt]


Handle all TOCs with one set of functions

--- src/catalog/layout/fop.clj
+++ src/catalog/layout/fop.clj
@@ -80,34 +80,23 @@
       (set/subset? items (set layout/subgenres))
       (keep items layout/subgenres))))

-(defn- toc-entry [items path]
-  [:fo:block (if (<= (count path) 2)
-               {:text-align-last "justify"}
-               {:text-align-last "justify" :start-indent "1em"})
-   [:fo:basic-link {:internal-destination (hash path)}
-    (str (layout/translations (last path)) " ")
-    [:fo:leader {:leader-pattern "dots"}]
-    [:fo:page-number-citation {:ref-id (hash path)}]]])
-
-(defn toc [items]
-  [:fo:block {:line-height "150%"}
-   (h1 :inhalt)
-   (keep #(toc-entry items [%]) (order (keys items)))])
-
-(defn- sub-toc-entry [items path]
-  [:fo:block
-   [:fo:block {:text-align-last "justify"}
-    [:fo:basic-link {:internal-destination (hash path)}
-     (str (layout/translations (last path)) " ")
-     [:fo:leader {:leader-pattern "dots"}]
-     [:fo:page-number-citation {:ref-id (hash path)}]]]
-   (when (map? items)
-     (keep #(toc-entry items (conj path %)) (order (keys items))))])
-
-(defn sub-toc [items path]
+(defn- toc-entry [items path to-depth]
+  (let [depth (count path)]
+    [:fo:block (if (<= depth 2)
+                 {:text-align-last "justify"}
+                 {:text-align-last "justify" :start-indent "1em"})
+     [:fo:basic-link {:internal-destination (hash path)}
+      (str (layout/translations (last path)) " ")
+      [:fo:leader {:leader-pattern "dots"}]
+      [:fo:page-number-citation {:ref-id (hash path)}]]
+     (when (and (map? items) (< depth to-depth))
+       (keep #(toc-entry (% items) (conj path %) to-depth) (order (keys items))))]))
+
+(defn toc [items path to-depth & {:keys [heading?]}]
   (when (map? items)
     [:fo:block {:line-height "150%"}
-     (keep #(sub-toc-entry (% items) (conj path %)) (order (keys items)))]))
+     (when heading? (h1 :inhalt))
+     (keep #(toc-entry (% items) (conj path %) to-depth) (order (keys items)))]))

 (def wrap (layout/wrapper ""))

@@ -301,7 +290,7 @@
 (defn format-sexp [items fmt]
   (when-let [items (fmt items)]
     [(h1 fmt)
-     (sub-toc items [fmt])
+     (toc items [fmt] 3)
      (set-marker (layout/translations fmt))
      (case fmt
        (:hörfilm :ludo) (entries-sexp items)


in case gelf isnt used

in case gelf isnt used

--- src/celestial/api/jobs.clj
+++ src/celestial/api/jobs.clj
@@ -16,9 +16,9 @@
     [flatland.useful.map :refer  (map-vals)]
     [swag.model :refer (defmodel)]
     [clojure.core.strint :refer (<<)]
-    [celestial.common :refer (bad-req success import-logging get!)]
+    [celestial.common :refer (bad-req success import-logging get*)]
     [gelfino.timbre :refer (get-tid)]
-    [celestial.persistency :as p ]
+    [celestial.persistency :as p]
     [celestial.persistency.actions :refer (find-action-for)]
     [celestial.jobs :as jobs]
     [swag.core :refer (GET- POST- PUT- DELETE- defroutes- errors)]
@@ -30,7 +30,7 @@
 (defmodel hash)

 (defn tid-link [tid]
-  (let [{:keys [host type]} (get! :celestial :log :gelf)]
+  (when-let [{:keys [host type]} (get* :celestial :log :gelf)]
     (case type
      :kibana (<< "http://~{host}/index.html#/dashboard/script/logstash.js?query=tid:~{tid}&fields=@timestamp,message,tid,")
      :gralog2 "TBD"


(SERVER-18) Manually checking DN validity

--- src/clj/puppetlabs/services/request_handler/request_handler_core.clj
+++ src/clj/puppetlabs/services/request_handler/request_handler_core.clj
@@ -120,11 +120,13 @@
                 "but the Puppet Server global config option allow-header-cert-info"
                 "was either not set, or was set to false. This header will be ignored."))
     (if (and (:allow-header-cert-info config) header-dn)
-      (try
+      (if (ssl/valid-x500-name? header-dn)
         (conj jruby-req {:client-cert-cn (ssl/x500-name->CN header-dn)
                          :client-cert    nil
                          :authenticated  (= "SUCCESS" header-auth)})
-        (catch AssertionError _
+        (do
+          (log/errorf "The DN '%s' provided by the HTTP header '%s' is malformed."
+                      header-dn (:ssl-client-header config))
           (conj jruby-req {:client-cert-cn nil
                            :client-cert    nil
                            :authenticated  false})))


Add some description on how to use the opt-out script.

--- src/oc/storage/util/inbox_opt_out.clj
+++ src/oc/storage/util/inbox_opt_out.clj
@@ -1,6 +1,14 @@
 (ns oc.storage.util.inbox-opt-out
   "
   Convert all the entries from opt-in to opt-out.
+
+  NB: before running this script you need to add this key:
+
+  (schema/optional-key :follow) schema/Bool
+
+  to the UserVisibility schema in oc.storage.resources.common
+  or the update will fail when reading the source entry.
+

   Usage:


CMR-1867 Refactors expected conversions, documents split-temporals.

--- umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
+++ umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
@@ -3,6 +3,14 @@
   writing it to an XML format and parsing it back. Conversion from a UMM record into metadata
   can be lossy if some fields are not supported by that format"
   (:require [cmr.umm-spec.models.common :as cmn]))
+
+(defmulti convert-internal
+  (fn [umm-coll metadata-format]
+    metadata-format))
+
+(defmethod convert-internal :default
+  [umm-coll _]
+  umm-coll)

 ;;; Utililty Functions

@@ -37,6 +45,8 @@
   (update-in t1 [:RangeDateTime] concat (:RangeDateTime t2)))

 (defn split-temporals
+  "Returns a seq of temporal extents with a new extent for each value under key
+  k (e.g. :RangeDateTime) in each source temporal extent."
   [k temporal-extents]
   (reduce (fn [result extent]
             (if-let [values (get extent k)]
@@ -50,23 +60,8 @@

 ;; ECHO 10

-(def not-implemented-fields
-  "This is a list of required but not implemented fields."
-  #{:DataLineage :MetadataStandard :Platform :ProcessingLevel :RelatedUrl
-    :ResponsibleOrganization :ScienceKeyword :SpatialExtent})
-
-(defn- dissoc-not-implemented-fields
-  "Removes not implemented fields since they can't be used for comparison"
-  [record]
-  (reduce (fn [r field]
-            (assoc r field nil))
-          record
-          not-implemented-fields))
-
-
-(defn- expected-echo10
-  "This manipulates the expected parsed UMM record based on lossy conversion in ECHO10."
-  [umm-coll]
+(defmethod convert-internal :echo10
+  [umm-coll _]
   ;; ECHO10 returns entry id as a combination of short name and version. It generates short name
   ;; from entry id. So the expected entry id when going from umm->echo10->umm is the original
   ;; entry id concatenated with the version id.
@@ -95,9 +90,8 @@
        ;; Then make sure we get the right record type out.
        (map cmn/map->TemporalExtentType)))

-(defn expected-dif
-  "Returns a UMM record with only features supported by DIF 9."
-  [umm-coll]
+(defmethod convert-internal :dif
+  [umm-coll _]
   (update-in umm-coll [:TemporalExtent] dif-temporal))

 ;; ISO 19115-2
@@ -111,26 +105,33 @@
        (split-temporals :SingleDateTime)
        (map cmn/map->TemporalExtentType)))

-(defn expected-iso-19115-2
-  [iso-coll]
-  (update-in iso-coll [:TemporalExtent] expected-iso-19115-2-temporal))
+(defmethod convert-internal :iso19115
+  [umm-coll _]
+  (update-in umm-coll [:TemporalExtent] expected-iso-19115-2-temporal))

-;;; Conversion Lookup By Format
+(defmethod convert-internal :iso-smap
+  [umm-coll _]
+  (convert-internal umm-coll :iso19115))

-(def ^:private formats->expected-conversion-fns
-  "A map of metadata formats to expected conversion functions"
-  {:echo10   expected-echo10
-   :iso19115 expected-iso-19115-2
-   :iso-smap expected-iso-19115-2
-   :dif      expected-dif})
+;;; Unimplemented Fields
+
+(def not-implemented-fields
+  "This is a list of required but not implemented fields."
+  #{:DataLineage :MetadataStandard :Platform :ProcessingLevel :RelatedUrl
+    :ResponsibleOrganization :ScienceKeyword :SpatialExtent})
+
+(defn- dissoc-not-implemented-fields
+  "Removes not implemented fields since they can't be used for comparison"
+  [record]
+  (reduce (fn [r field]
+            (assoc r field nil))
+          record
+          not-implemented-fields))

 ;;; Public API

 (defn convert
-  "Returns input-record transformed according to the specified
-  transformation for metadata-format."
-  [input-record metadata-format]
-  (let [f (get formats->expected-conversion-fns
-               metadata-format
-               identity)]
-    (dissoc-not-implemented-fields (f input-record))))
+  "Returns input UMM-C record transformed according to the specified transformation for
+  metadata-format."
+  [umm-coll metadata-format]
+  (dissoc-not-implemented-fields (convert-internal umm-coll metadata-format)))


CMR-1863 Updates expected-conversion for Platform Characteristics in DIF 9

--- umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
+++ umm-spec-lib/src/cmr/umm_spec/test/expected_conversion.clj
@@ -102,8 +102,8 @@
   [umm-coll _]
   (-> umm-coll
       (update-in [:TemporalExtents] dif-temporal)
-      ;; DIF 9 does not support Platform Type.
-      (update-in-each [:Platforms] assoc :Type nil)))
+      ;; DIF 9 does not support Platform Type or Characteristics.
+      (update-in-each [:Platforms] assoc :Type nil :Characteristics nil)))

 ;; DIF 10


refactor after running kibit

refactor after running kibit

--- src/refactor_nrepl/ns/constructor.clj
+++ src/refactor_nrepl/ns/constructor.clj
@@ -28,8 +28,7 @@
                       (remove nil?))]
     (if (some #{:all} referred)
       :all
-      (if (empty? referred)
-        nil
+      (when (seq referred)
         (-> referred concat flatten distinct)))))

 (defn- get-flags [libspecs]
@@ -63,7 +62,7 @@
                    " ["
                    "")]
       (str name suffix))
-    (-> dep name)))
+    (name dep)))

 (defn- dependency-comparator
   "Lexicographical comparison of dependency vectors based on the name
@@ -123,27 +122,27 @@

 (defn- create-libspec-vectors-without-prefix
   [libspecs]
-  (-> (for [libspec libspecs]
-        (create-libspec (update-in libspec [:ns] #(-> % suffix symbol))))
-      vec))
+  (vec
+   (for [libspec libspecs]
+         (create-libspec (update-in libspec [:ns] #(-> % suffix symbol))))))

 (defn- create-libspec-vectors-with-prefix
   [libspecs]
-  (-> (for [libspec libspecs]
-        (create-libspec libspec))
-      vec))
+  (vec
+   (for [libspec libspecs]
+         (create-libspec libspec))))

 (defn- create-prefixed-libspec-vector
   [libspecs]
-  (-> (for [{:keys [ns] :as libspec} libspecs]
-        (create-libspec (assoc libspec :ns (ns-suffix ns))))
-      vec))
+  (vec
+   (for [{:keys [ns] :as libspec} libspecs]
+         (create-libspec (assoc libspec :ns (ns-suffix ns))))))

 (defn- create-prefixed-libspec-vectors
   [[libspec & more :as libspecs]]
   (if-not more
     (create-libspec-vectors-with-prefix [libspec])
-    [(into [(ns-prefix (-> libspecs first))]
+    [(into [(ns-prefix (first libspecs))]
            (create-libspec-vectors-without-prefix libspecs))]))

 (defn- create-libspec-vectors
@@ -160,7 +159,7 @@
                       by-prefix
                       create-libspec-vectors
                       sort-libspecs)]
-    (when-not (empty? libspecs)
+    (when (seq libspecs)
       (cons :require libspecs))))

 (defn- classes-by-prefix
@@ -198,7 +197,7 @@
                          classes-by-prefix
                          create-import-components
                          sort-imports)]
-    (when-not (empty? import-form)
+    (when (seq import-form)
       (cons :import import-form))))

 (defn- drop-index [col idx]


* src/clj/cljs/compiler.clj: store file information for defs in cljs.compiler/namespaces


add coercion rules for commodity sales

add coercion rules for commodity sales


Monkey-patch goog.require like cljs.browser.repl does.


Provide a wrapper around SharedPreferences


monte carlo pi

monte carlo pi

--- src/maths/pi.clj
+++ src/maths/pi.clj
@@ -15,16 +15,39 @@
   (* 4.0
     (r/fold +' (map / (cycle [1 -1]) (take iterations odd-numbers))))))

+
+(calculate-pi 1000)
+(calculate-pi2 1000)
+
 ;(calculate-pi2 100)

-; (take 5  (map (filter odd? (iterate inc 1)))
+; (println "calculated pi =" (calculate-pi 100000))
+; (println "Math/PI =" Math/PI)

-(println "calculated pi =" (calculate-pi 100000))
-(println "Math/PI =" Math/PI)

-(def odd-numbers (filter odd? (iterate inc 1)))
-(def cycled-odd (lazy-seq (map cycle [1 -1] odd-numbers)))

-(take 5 cycled-odd)
+; pi monte carlo method

-(calculate-pi 1000)
+; A Monte Carlo Simulation is a way of approximating the value of a function
+; where calculating the actual value is difficult or impossible.
+
+; It uses random sampling to define constraints on the value
+; and then makes a sort of "best guess."
+
+; A simple Monte Carlo Simulation can be used to calculate the value for π. If you had a circle and a square where the length of a side of the square was the same as the diameter of the circle, the ratio of the area of the circle to the area of the square would be π/4.
+
+; So, if you put this circle inside the square and
+; select many random points inside the square,
+; the number of points inside the circle divided by
+; the number of points inside the square
+; and the circle would be approximately π/4.
+
+(defn calc-pi [iterations]
+  (loop [x (rand) y (rand) in 0 total 1]
+    (if (< total iterations)
+      (recur (rand) (rand) (if (<= (+ (* x x) (* y y)) 1) (inc in) in) (inc total))
+      (double (* (/ in total) 4)))))
+
+(calc-pi 100000)
+
+; (doseq [x (take 5 (iterate #(* 10 %) 10))] (println (str (format "% 8d" x) ": " (calc-pi x))))


Fix async-pmap bugs

Fix async-pmap bugs

--- src/npm/mvn.cljs
+++ src/npm/mvn.cljs
@@ -93,11 +93,20 @@
    xs []))

 (defn async-pmap [f xs done]
-  (let [cnt (atom (count xs))]
-    (reduce-kv (fn [rets i x]
+  (let [cnt  (atom (count xs))
+        rets (atom (vec (repeat @cnt nil)))]
+    (reduce-kv (fn [_ i x]
                  (f x (fn [ret]
-                        (assoc rets i ret)
+                        (swap! rets assoc i ret)
                         (when (zero? (swap! cnt dec))
-                          (done rets)))))
-               (vec (repeat @cnt nil))
+                          (done @rets)))))
+               nil
                (vec xs))))
+
+
+(comment
+  (async-pmap (fn [x done]
+                (done (inc x)))
+              (range 10)
+              (fn [xs] (println "Done:" xs)))
+  )


No need to assoc workflow to job. Since every job should have the key :workflow.

--- src/onyx/api.clj
+++ src/onyx/api.clj
@@ -152,7 +152,7 @@
    (submit-job peer-config job {:monitoring :no-op}))
   ([peer-config job monitoring-config]
    (try (validator/validate-peer-config peer-config)
-        (validator/validate-job (assoc job :workflow (:workflow job)))
+        (validator/validate-job job)
         (validator/validate-flow-conditions (:flow-conditions job) (:workflow job))
         (validator/validate-lifecycles (:lifecycles job) (:catalog job))
         (validator/validate-windows (:windows job) (:catalog job))


Increment version number

Increment version number

--- src/clj/nightcode/core.clj
+++ src/clj/nightcode/core.clj
@@ -51,7 +51,7 @@
                        :auto-save? true})
     (swap! pref-state update :expansion-set u/filter-paths)
     (doto stage
-      (.setTitle "Nightcode 2.5.1")
+      (.setTitle "Nightcode 2.5.2")
       (.setScene scene)
       (.show))
     (shortcuts/init-tabs! scene)


cupons: fix logo

cupons: fix logo

--- src/clojure/nossal/web.clj
+++ src/clojure/nossal/web.clj
@@ -142,7 +142,7 @@
             [:amp-analytics {:type "googleanalytics"}
              [:script {:type "application/json"} dat/data-analytics]]
             [:section
-             [:amp-img {:src (format "/images/%s_logo.png" service) :alt (str service " logo") :height "100" :width "265"}]
+             [:amp-img {:src (format "/images/%s_logo.png" (s/lower-case service)) :alt (str service " logo") :height "100" :width "265"}]
              [:h1 "Cupom de desconto " (s/capitalize service) "."]
              [:div.intro
                [:p (cdata :text)]]


function to add a development user

function to add a development user

--- dev/user.clj
+++ dev/user.clj
@@ -11,6 +11,7 @@
             [clojure.test :refer :all]
             [tictag.server-test :as server-test]
             [tictag.ragtime :as ragtime]
+            [tictag.tagtime :as tt]
             [ragtime.repl :refer [migrate rollback]]
             [honeysql.core :as sql]
             [honeysql.helpers :refer :all]
@@ -30,4 +31,60 @@

 (reloaded.repl/set-init! #(tictag.system/system config))

-(defn dev-user [] (db/get-user (:db system) "j"))
+(defn dev-user [] (db/get-user (:db system) "TEST_USER"))
+
+(def my-user-id (-> (select :id)
+                    (from :users)
+                    (where [:= :username "TEST_USER"])
+                    (limit 1)))
+
+(def my-beeminder-id (-> (select :beeminder.id)
+                         (from :beeminder)
+                         (join :users [:= :users.id my-user-id])))
+
+(defn make-dev-user []
+  (j/with-db-transaction [tx (-> system :db :db)]
+    (j/execute! tx
+                (-> (insert-into :users)
+                    (values [{:username "TEST_USER"
+                              :email    "tictag-test@agh.io"
+                              :pass     (db/hashp "foobar")
+                              :tz       "America/Los_Angeles"}])
+                    (upsert (-> (on-conflict :username)
+                                (do-nothing)))
+                    sql/format))
+    (let [{:keys [encrypted iv]} (crypto/encrypt (env :dev-beeminder-token)
+                                                 (-> system :db :crypto-key))]
+      (j/execute! tx
+                  (-> (insert-into :beeminder)
+                      (values [{:user_id         my-user-id
+                                :username        "tictagtest"
+                                :encrypted_token encrypted
+                                :encryption_iv   iv
+                                :is_enabled      true}])
+                      (upsert (-> (on-conflict :user_id)
+                                  (do-update-set :encrypted_token :encryption_iv)))
+                      sql/format)))
+    (let [{:keys [encrypted iv]} (crypto/encrypt
+                                  (env :dev-slack-bot-token)
+                                  (-> system :db :crypto-key))]
+      (j/execute! tx
+                  (-> (insert-into :slack)
+                      (values [{:user_id                    my-user-id
+                                :username                   (env :dev-slack-username)
+                                :encrypted_bot_access_token encrypted
+                                :encryption_iv              iv
+                                :channel_id                 (env :dev-slack-channel-id)
+                                :slack_user_id              (env :dev-slack-user-id)}])
+                      (upsert (-> (on-conflict :user_id)
+                                  (do-update-set :encrypted_bot_access_token
+                                                 :encryption_iv)))
+                      sql/format)))
+    (j/execute! tx
+                (-> (insert-into :beeminder_goals)
+                    (values [{:beeminder_id my-beeminder-id
+                              :goal         "test"
+                              :tags         (pr-str [:and :this :that])}])
+                    (upsert (-> (on-conflict :beeminder_id :goal)
+                                (do-nothing)))
+                    sql/format))))


Add fns to highlight text passages

Add fns to highlight text passages

--- src/discuss/texts/lib.cljs
+++ src/discuss/texts/lib.cljs
@@ -1,6 +1,8 @@
 (ns discuss.texts.lib
   (:require [cljs.spec.alpha :as s]
             [clojure.string :as string]
+            [goog.string :refer [format]]
+            [goog.string.format]
             [discuss.translations :refer [translate] :rename {translate t}]))

 (defn join-with-and
@@ -11,3 +13,17 @@
   :args (s/cat :col (s/coll-of string?))
   :ret string?)

+
+;; -----------------------------------------------------------------------------
+;; Highlight strings with html classes
+
+(defn highlight-premise [premise]
+  (format "%s" premise))
+
+(defn highlight-conclusion [premise]
+  (format "%s" premise))
+
+(defn highlight-undercut [premise]
+  (format "%s" premise))
+
+


loading integration in core

loading integration in core

--- src/distributions/core.clj
+++ src/distributions/core.clj
@@ -18,4 +18,5 @@
 (load "t-distribution")
 (load "normal-laplace")
 (load "polya-gamma")
+(load "integration")


Adds multirember

Adds multirember

--- src/the_little_clojurian/chapter3.clj
+++ src/the_little_clojurian/chapter3.clj
@@ -99,3 +99,15 @@
          '()))
   (is (= (subst2 'vanilla 'chocolate 'banana '(banana ice cream with chocolate topping))
          '(vanilla ice cream with chocolate topping))))
+
+
+(with-test
+  (def multirember
+    (fn [a lat]
+      (cond (null? lat) '()
+            (eq? (car lat) a) (multirember a (cdr lat))
+            :else (cons (car lat) (multirember a (cdr lat))))))
+  (is (= (multirember 'cup '())
+         '()))
+  (is (= (multirember 'cup '(coffee cup tea cup and hick cup))
+         '(coffee tea and hick))))


Adding more functions to basket store

Adding more functions to basket store

--- src/eatme/basket_store.clj
+++ src/eatme/basket_store.clj
@@ -4,7 +4,8 @@
             [monger.core :as m]
             [monger.collection :as mc]
             [monger.joda-time]
-            [monger.operators :refer :all])
+            [monger.operators :refer :all]
+            [monger.conversion :refer :all])
   (:import [org.bson.types ObjectId]
            [com.mongodb DB WriteConcern]))

@@ -15,7 +16,7 @@
   (m/set-db! (m/get-db))) ;; db name is in the uri

 (defn load-basket [id]
-  (let [basket-id (ObjectId. id)]
+  (let [basket-id (to-object-id id)]
     (assoc (mc/find-map-by-id "basket" basket-id)
       :items (mc/find-maps "items" {:_basket basket-id}))))

@@ -31,9 +32,19 @@
     (str id)))

 (defn add-item! [basket-id item]
-  (let [id (ObjectId. basket-id)]
-    (mc/insert "items" (merge {:_id id :_basket (ObjectId. basket-id)} item))
+  (let [id (ObjectId.)]
+    (mc/insert "items" (merge {:_id id :_basket (to-object-id basket-id)} item))
     (str id)))

+(def item-updateable #{:name :qty})
+
+(defn update-item! [item]
+  (mc/update-by-id "items" (to-object-id (:_id item)) {$set (select-keys item item-updateable)}))
+
+(defn delete-item! [item-id]
+  (mc/remove-by-id "items" (to-object-id item-id)))
+
 (defn delete-basket! [id]
-  (mc/remove "basket" {:_id (ObjectId. id)}))
+  (let [basket-id (ObjectId. id)]
+    (mc/remove-by-id "basket" basket-id)
+    (mc/remove "items" {:_basket basket-id})))


improved buffer creating; some cleanup

improved buffer creating; some cleanup

--- src/engine/data/buffer.clj
+++ src/engine/data/buffer.clj
@@ -1,5 +1,6 @@
 (ns engine.data.buffer
-  (:use engine.data.mode)
+  (:use engine.data.mode
+        [engine.server.command :only (command)])
   (:require [engine.data.rope :as rope]
             [engine.data.cursor :as cursor]
             [clojure.tools.logging :as log])
@@ -19,7 +20,7 @@
                           [@state (cursor/pos state)])),
           [row column] (apply rope/translate state)]
       {:response (conj (or (transfn pre-state state name) [])
-                       {:command "move-to-position" :args {:row row :column column}})}))
+                       (command "move-to-position" {:row row :column column}))}))
   (trans [this actionfn] (trans this actionfn (fn [& _] nil)))

   (inputfn [this]
@@ -29,20 +30,28 @@
   (deref [_] @cursor))

 (defmethod print-method Buffer [buffer writer]
-  (print-simple (format "#<Buffer %s -> %s>" (:name buffer) (pr-str @ buffer)) writer))
-
-(defn buffer [name ^Cursor cursor updatefn keymapfn]
-  (Buffer. name cursor updatefn keymapfn nil nil))
-
-(defn buffer? [x]
-  (instance? Buffer x))
+  (print-simple (format "#<Buffer %s -> %s>" (:name buffer) (pr-str (:cursor buffer))) writer))

 (defn sender [^Agent buffers name]
   (fn [state]
     (send buffers assoc name state)))

+(defn buffer [name updatefn & {:keys [cursor keymapfn mode file]
+                               :or {cursor (cursor/cursor "" 0) keymapfn fundamental-mode-keymap}}]
+  (Buffer. name cursor updatefn keymapfn mode file))
+
+
+(defn buffer? [x]
+  (instance? Buffer x))
+
+(defn create-buffer [^Agent buffers name & args]
+  (send buffers assoc name (apply buffer name (sender buffers name) args))
+  (@buffers name))
+
 (defn loader [^Agent buffers]
   (fn [[name] _]
+    (when-not (@buffers name)
+      (create-buffer buffers name))
     (let [cursor (get-in @buffers [name :cursor]),
           rope @cursor,
           [row column] (rope/translate rope (cursor/pos cursor))]
@@ -60,6 +69,5 @@
   ([]
      (let [buffers (buffers {}),
            name "*scratch*"]
-       (send buffers assoc name
-             (buffer name (cursor/cursor "This is the scratch buffer." 0) (sender buffers name) fundamental-mode-keymap))
+       (create-buffer buffers "*scratch*" :cursor (cursor/cursor "This is the scratch buffer." 0))
        buffers)))


grey out previous/next buttons

grey out previous/next buttons

--- src/frontend/renderer.cljs
+++ src/frontend/renderer.cljs
@@ -116,14 +116,24 @@
     om/IRender
     (render [this]
       (if (:signed-in? data)
-        (dom/button #js {:onClick core/previous-track} "Previous")))))
+        (if-not (nil? (:current-track-id data))
+          (if (< 0
+                 (.-tracknum (core/get-music-info-from-id (:current-track-id data))))
+            (dom/button #js {:onClick core/previous-track} "Previous")
+            (dom/button #js {:onClick core/previous-track :disabled true} "Previous"))
+          (dom/button #js {:onClick core/previous-track :disabled true} "Previous"))))))

 (defn next-track-button [data owner]
   (reify
     om/IRender
     (render [this]
       (if (:signed-in? data)
-        (dom/button #js {:onClick core/next-track} "Next")))))
+        (if-not (nil? (:current-track-id data))
+          (if (> (- (count (:music-info data)) 1)
+                (.-tracknum (core/get-music-info-from-id (:current-track-id data))))
+            (dom/button #js {:onClick core/next-track} "Next")
+            (dom/button #js {:onClick core/next-track :disabled true} "Next"))
+          (dom/button #js {:onClick core/next-track :disabled true} "Next"))))))

 (defn restart-button [data owner]
   (reify om/IRender


Remove unused whitespace

Remove unused whitespace

--- src/graphql_clj/parser.clj
+++ src/graphql_clj/parser.clj
@@ -4,8 +4,6 @@
             [camel-snake-kebab.core :refer [->kebab-case]]
             [clojure.string :as str]
             [clojure.set :as set]))
-
-(def whitespace (insta/parser "whitespace = #'\\s+'"))

 (def graphql-bnf "graphql.bnf")


Escape backslash before sending to Node.js process, addresses  Windows platform issues

--- src/clj/cljs/repl/node.clj
+++ src/clj/cljs/repl/node.clj
@@ -51,7 +51,8 @@
   "Evaluate a JavaScript string in the Node REPL process."
   [repl-env js]
   (let [{:keys [in out]} @(:socket repl-env)]
-    (write out js)
+    ;; escape backslash for Node.js under Windows
+    (write out (string/replace js "\\" "\\\\"))
     (let [result (json/read-str
                    (read-response in) :key-fn keyword)]
       (condp = (:status result)


renamed unused local _vars

renamed unused local _vars

--- src/clj_fakes/context.cljc
+++ src/clj_fakes/context.cljc
@@ -77,11 +77,11 @@
 any
   (reify
     ArgsMatcher
-    (args-match? [_ _args_] true)
+    (args-match? [_ _args] true)
     (args-matcher->str [_] "<any>")

     ImplicitArgMatcher
-    (arg-matches-implicitly? [_ _arg_] true)
+    (arg-matches-implicitly? [_ _arg] true)
     (arg-matcher->str [_] "<any>")))

 (defn ^:no-doc -arg-matches?
@@ -586,7 +586,7 @@
                members (filter #(and (= method-sym (:name %))
                                      (not (contains? (:flags %) :varargs)))
                                (:members interface))]
-           (map (fn [{:keys [return-type parameter-types] :as _member_}]
+           (map (fn [{:keys [return-type parameter-types] :as _member}]
                   {:method-sym (-with-hint return-type method-sym)
                    :arglist    (-fix-arglist-with-hints protocol-sym (into ['this] parameter-types))})
                 members))))))
@@ -668,7 +668,7 @@

 #?(:clj
    (defn ^:no-doc -emit-method-specs
-     [{:keys [imp-sym signatures] :as _method-imp_}]
+     [{:keys [imp-sym signatures] :as _method-imp}]
      (map (fn [{:keys [method-sym arglist]}]
             `(~method-sym ~arglist
                (~imp-sym ~@(map -remove-meta arglist))))
@@ -698,15 +698,15 @@

 #?(:clj
    (defn ^:no-doc -not-yet-in-specs?
-     [specs [nice-method-sym :as _spec_]]
+     [specs [nice-method-sym :as _spec]]
      (nil?
-       (-find-first (fn [[method-sym :as _spec_]]
+       (-find-first (fn [[method-sym :as _spec]]
                       (= method-sym nice-method-sym))
                     specs))))

 #?(:clj
    (defn ^:no-doc -add-nice-specs-for-protocol
-     [env [protocol-sym parsed-specs :as _parsed_spec_]]
+     [env [protocol-sym parsed-specs :as _parsed-spec]]
      (let [nice-specs (-nice-specs env protocol-sym)
            auto-specs (filter (partial -not-yet-in-specs? parsed-specs) nice-specs)
            new-specs (concat parsed-specs auto-specs)]
@@ -879,7 +879,7 @@
         (throw (ex-info (str "Function is expected to be never called. Actual calls:\n" (pr-str f-calls) ".") {})))))

 (defn ^:no-doc -call-matches?
-  [f args-matcher [call-f {:keys [args]} :as _call_]]
+  [f args-matcher [call-f {:keys [args]} :as _call]]
   (and (= f call-f)
        (args-match? args-matcher args)))

@@ -1018,7 +1018,7 @@
 (defn unpatch-all!
   "Restores original values for all the variables patched in the specified context."
   [ctx]
-  (doseq [[_var_ unpatch-fn] (:unpatches @ctx)]
+  (doseq [[_var unpatch-fn] (:unpatches @ctx)]
     (unpatch-fn)))

 ;;;;;;;;;;;;;;;;;;;;;;;; Utils
@@ -1029,7 +1029,7 @@
   2. on each call returns the next value from `coll`, cyclically."
   [coll]
   (let [vals (atom (cycle coll))]
-    (fn cyclical-fn [& _args_]
+    (fn cyclical-fn [& _args]
       (let [result (first @vals)]
         (swap! vals next)
         result))))


Add type annotations for producer

Add type annotations for producer

--- src/clj_kafka/producer.clj
+++ src/clj_kafka/producer.clj
@@ -9,19 +9,19 @@
    serializer.class : default is kafka.serializer.DefaultEncoder
    zk.connect       : Zookeeper connection. e.g. localhost:2181 "
   [m]
-  (Producer. (ProducerConfig. (as-properties m))))
+  ^Producer (Producer. (ProducerConfig. (as-properties m))))

 (defn message
   "Creates a message with the specified payload.
    payload : bytes for the message payload. e.g. (.getBytes \"hello, world\")"
-  [payload]
+  [^:bytes payload]
   (Message. payload))

 (defn send-messages
   "Sends a message.
    topic   : a string
    msgs    : a single message, or sequence of messages to send"
-  [producer topic msgs]
+  [^Producer producer ^String topic msgs]
   (.send producer (ProducerData. topic msgs)))


Updated Forecast

Updated Forecast

--- src/cljc/witan/schema.cljc
+++ src/cljc/witan/schema.cljc
@@ -105,13 +105,14 @@

 (def Forecast
   "Forecast"
-  {(s/required-key :forecast-id)   IdType
+  {(s/required-key :id)            IdType
    (s/required-key :name)          s/Str
    (s/required-key :owner)         IdType
+   (s/required-key :series-id)     IdType
    (s/required-key :version)       s/Int
-   (s/required-key :last-modified) DateTimeType
-   (s/required-key :last-modifier) IdType
+   (s/required-key :created)       DateTimeType
    (s/required-key :in-progress?)  s/Bool
+   (s/required-key :descendant-id) (s/maybe IdType)
    (s/optional-key :description)   s/Str
    (s/optional-key :tag)           Tag})


Limiting the number of reminders shown (#118)

--- src/cljs/memento/core.cljs
+++ src/cljs/memento/core.cljs
@@ -29,7 +29,6 @@
 ;;;; Data and helpers
 ;;;;------------------------------

-
 (adapt-bootstrap Button)
 (adapt-bootstrap ButtonGroup)
 (adapt-bootstrap DropdownButton)
@@ -56,6 +55,7 @@

 (def top-div-target (find-dom-elem :#header))

+(def max-reminders 5)

 ;;;;------------------------------
 ;;;; Queries
@@ -238,7 +238,10 @@

 (defn reminder-section [is-focused?]
-  (let [reminders (<sub [:cache :reminders])
+  ;; Take the maximum number of reminders here, instead of when we call for the
+  ;; list, because this allows me to display more as the user marks them as viewed
+  ;; without having to make a backend call.
+  (let [reminders (take max-reminders (<sub [:cache :reminders]))
         showing?  (<sub [:ui-state :show-reminders?])]
     ;; We will only show the reminder notification when we aren't focused
     ;; on elaborating a thought


Put each terrain and unit's info into one line

--- src/cljs/zetawar/data.cljs
+++ src/cljs/zetawar/data.cljs
@@ -642,902 +642,311 @@
     :description "Sterling's Aruba"
     :terrains
     [;; Row 1
-     {:q 1
-      :r 0
-      :terrain-type :plains}
-     {:q 2
-      :r 0
-      :terrain-type :plains}
-     {:q 3
-      :r 0
-      :terrain-type :mountains}
-     {:q 4
-      :r 0
-      :terrain-type :deep-water}
-     {:q 5
-      :r 0
-      :terrain-type :deep-water}
-     {:q 6
-      :r 0
-      :terrain-type :deep-water}
+     {:q 1 :r 0 :terrain-type :plains}
+     {:q 2 :r 0 :terrain-type :plains}
+     {:q 3 :r 0 :terrain-type :mountains}
+     {:q 4 :r 0 :terrain-type :deep-water}
+     {:q 5 :r 0 :terrain-type :deep-water}
+     {:q 6 :r 0 :terrain-type :deep-water}
      ;; Row 2
-     {:q 0
-      :r 1
-      :terrain-type :plains}
-     {:q 1
-      :r 1
-      :terrain-type :mountains}
-     ;; Multiplayer base location
-     {:q 2
-      :r 1
-      :terrain-type :plains}
-     {:q 3
-      :r 1
-      :terrain-type :plains}
-     {:q 4
-      :r 1
-      :terrain-type :mountains}
-     {:q 5
-      :r 1
-      :terrain-type :woods}
-     {:q 6
-      :r 1
-      :terrain-type :deep-water}
-     {:q 7
-      :r 1
-      :terrain-type :deep-water}
+     {:q 0 :r 1 :terrain-type :plains}
+     {:q 1 :r 1 :terrain-type :mountains}
+     {:q 2 :r 1 :terrain-type :plains} ;; Multiplayer base location
+     {:q 3 :r 1 :terrain-type :plains}
+     {:q 4 :r 1 :terrain-type :mountains}
+     {:q 5 :r 1 :terrain-type :woods}
+     {:q 6 :r 1 :terrain-type :deep-water}
+     {:q 7 :r 1 :terrain-type :deep-water}
      ;; Row 3
-     {:q 0
-      :r 2
-      :terrain-type :mountains}
-     ;; Multiplayer base location
-     {:q 1
-      :r 2
-      :terrain-type :plains}
-     {:q 2
-      :r 2
-      :terrain-type :plains}
-     {:q 3
-      :r 2
-      :terrain-type :plains}
-     {:q 4
-      :r 2
-      :terrain-type :plains}
-     {:q 5
-      :r 2
-      :terrain-type :woods}
-     {:q 6
-      :r 2
-      :terrain-type :mountains}
-     {:q 7
-      :r 2
-      :terrain-type :mountains}
-     {:q 8
-      :r 2
-      :terrain-type :deep-water}
+     {:q 0 :r 2 :terrain-type :mountains}
+     {:q 1 :r 2 :terrain-type :plains} ;; Multiplayer base location
+     {:q 2 :r 2 :terrain-type :plains}
+     {:q 3 :r 2 :terrain-type :plains}
+     {:q 4 :r 2 :terrain-type :plains}
+     {:q 5 :r 2 :terrain-type :woods}
+     {:q 6 :r 2 :terrain-type :mountains}
+     {:q 7 :r 2 :terrain-type :mountains}
+     {:q 8 :r 2 :terrain-type :deep-water}
      ;; Row 4
-     {:q 0
-      :r 3
-      :terrain-type :plains}
-     {:q 1
-      :r 3
-      :terrain-type :plains}
-     {:q 2
-      :r 3
-      :terrain-type :plains}
-     {:q 3
-      :r 3
-      :terrain-type :plains}
-     {:q 4
-      :r 3
-      :terrain-type :woods}
-     {:q 5
-      :r 3
-      :terrain-type :plains}
-     {:q 6
-      :r 3
-      :terrain-type :plains}
-     {:q 7
-      :r 3
-      :terrain-type :mountains}
-     {:q 8
-      :r 3
-      :terrain-type :deep-water}
+     {:q 0 :r 3 :terrain-type :plains}
+     {:q 1 :r 3 :terrain-type :plains}
+     {:q 2 :r 3 :terrain-type :plains}
+     {:q 3 :r 3 :terrain-type :plains}
+     {:q 4 :r 3 :terrain-type :woods}
+     {:q 5 :r 3 :terrain-type :plains}
+     {:q 6 :r 3 :terrain-type :plains}
+     {:q 7 :r 3 :terrain-type :mountains}
+     {:q 8 :r 3 :terrain-type :deep-water}
      ;; Row 5
-     {:q 0
-      :r 4
-      :terrain-type :deep-water}
-     ;; Multiplayer base location
-     {:q 1
-      :r 4
-      :terrain-type :mountains}
-     {:q 2
-      :r 4
-      :terrain-type :plains}
-     {:q 3
-      :r 4
-      :terrain-type :plains}
-     {:q 4
-      :r 4
-      :terrain-type :plains}
-     {:q 5
-      :r 4
-      :terrain-type :plains}
-     {:q 6
-      :r 4
-      :terrain-type :plains}
-     {:q 7
-      :r 4
-      :terrain-type :plains}
-     {:q 8
-      :r 4
-      :terrain-type :deep-water}
+     {:q 0 :r 4 :terrain-type :deep-water}
+     {:q 1 :r 4 :terrain-type :mountains} ;; Multiplayer base location
+     {:q 2 :r 4 :terrain-type :plains}
+     {:q 3 :r 4 :terrain-type :plains}
+     {:q 4 :r 4 :terrain-type :plains}
+     {:q 5 :r 4 :terrain-type :plains}
+     {:q 6 :r 4 :terrain-type :plains}
+     {:q 7 :r 4 :terrain-type :plains}
+     {:q 8 :r 4 :terrain-type :deep-water}
      ;; Row 6
-     {:q 0
-      :r 5
-      :terrain-type :deep-water}
-     {:q 1
-      :r 5
-      :terrain-type :plains}
-     {:q 2
-      :r 5
-      :terrain-type :plains}
-     {:q 3
-      :r 5
-      :terrain-type :woods}
-     {:q 4
-      :r 5
-      :terrain-type :plains}
-     {:q 5
-      :r 5
-      :terrain-type :plains}
-     {:q 6
-      :r 5
-      :terrain-type :plains}
-     {:q 7
-      :r 5
-      :terrain-type :plains}
-     {:q 8
-      :r 5
-      :terrain-type :deep-water}
+     {:q 0 :r 5 :terrain-type :deep-water}
+     {:q 1 :r 5 :terrain-type :plains}
+     {:q 2 :r 5 :terrain-type :plains}
+     {:q 3 :r 5 :terrain-type :woods}
+     {:q 4 :r 5 :terrain-type :plains}
+     {:q 5 :r 5 :terrain-type :plains}
+     {:q 6 :r 5 :terrain-type :plains}
+     {:q 7 :r 5 :terrain-type :plains}
+     {:q 8 :r 5 :terrain-type :deep-water}
      ;; Row 7
-     {:q 0
-      :r 6
-      :terrain-type :deep-water}
-     {:q 1
-      :r 6
-      :terrain-type :mountains}
-     {:q 2
-      :r 6
-      :terrain-type :plains}
-     {:q 3
-      :r 6
-      :terrain-type :woods}
-     {:q 4
-      :r 6
-      :terrain-type :plains}
-     {:q 5
-      :r 6
-      :terrain-type :plains}
-     {:q 6
-      :r 6
-      :terrain-type :plains}
-     ;; Multiplayer base location
-     {:q 7
-      :r 6
-      :terrain-type :plains}
-     {:q 8
-      :r 6
-      :terrain-type :mountains}
+     {:q 0 :r 6 :terrain-type :deep-water}
+     {:q 1 :r 6 :terrain-type :mountains}
+     {:q 2 :r 6 :terrain-type :plains}
+     {:q 3 :r 6 :terrain-type :woods}
+     {:q 4 :r 6 :terrain-type :plains}
+     {:q 5 :r 6 :terrain-type :plains}
+     {:q 6 :r 6 :terrain-type :plains}
+     {:q 7 :r 6 :terrain-type :plains} ;; Multiplayer base location
+     {:q 8 :r 6 :terrain-type :mountains}
      ;; Row 8
-     {:q 0
-      :r 7
-      :terrain-type :deep-water}
-     {:q 1
-      :r 7
-      :terrain-type :mountains}
-     {:q 2
-      :r 7
-      :terrain-type :woods}
-     {:q 3
-      :r 7
-      :terrain-type :mountains}
-     {:q 4
-      :r 7
-      :terrain-type :plains}
-     ;; Multiplayer base location
-     {:q 5
-      :r 7
-      :terrain-type :plains}
-     {:q 6
-      :r 7
-      :terrain-type :mountains}
-     {:q 7
-      :r 7
-      :terrain-type :plains}
+     {:q 0 :r 7 :terrain-type :deep-water}
+     {:q 1 :r 7 :terrain-type :mountains}
+     {:q 2 :r 7 :terrain-type :woods}
+     {:q 3 :r 7 :terrain-type :mountains}
+     {:q 4 :r 7 :terrain-type :plains}
+     {:q 5 :r 7 :terrain-type :plains} ;; Multiplayer base location
+     {:q 6 :r 7 :terrain-type :mountains}
+     {:q 7 :r 7 :terrain-type :plains}
      ;; Row 9
-     {:q 1
-      :r 8
-      :terrain-type :deep-water}
-     {:q 2
-      :r 8
-      :terrain-type :deep-water}
-     {:q 3
-      :r 8
-      :terrain-type :deep-water}
-     {:q 4
-      :r 8
-      :terrain-type :deep-water}
-     ;; Multiplayer base location
-     {:q 5
-      :r 8
-      :terrain-type :mountains}
-     {:q 6
-      :r 8
-      :terrain-type :plains}
-     {:q 7
-      :r 8
-      :terrain-type :plains}
-     ]
-    }
+     {:q 1 :r 8 :terrain-type :deep-water}
+     {:q 2 :r 8 :terrain-type :deep-water}
+     {:q 3 :r 8 :terrain-type :deep-water}
+     {:q 4 :r 8 :terrain-type :deep-water}
+     {:q 5 :r 8 :terrain-type :mountains} ;; Multiplayer base location
+     {:q 6 :r 8 :terrain-type :plains}
+     {:q 7 :r 8 :terrain-type :plains}]}
+
    :city-sprawl
    {:id :city-sprawl
     :description "City Sprawl"
     :terrains
     [;; Row 1
-     {:q 2
-      :r 0
-      :terrain-type :woods}
-     {:q 3
-      :r 0
-      :terrain-type :woods}
-     {:q 4
-      :r 0
-      :terrain-type :woods}
-     {:q 5
-      :r 0
-      :terrain-type :woods}
-     {:q 6
-      :r 0
-      :terrain-type :woods}
-     {:q 10
-      :r 0
-      :terrain-type :woods}
-     {:q 11
-      :r 0
-      :terrain-type :woods}
-     {:q 12
-      :r 0
-      :terrain-type :woods}
+     {:q 2 :r 0 :terrain-type :woods}
+     {:q 3 :r 0 :terrain-type :woods}
+     {:q 4 :r 0 :terrain-type :woods}
+     {:q 5 :r 0 :terrain-type :woods}
+     {:q 6 :r 0 :terrain-type :woods}
+     {:q 10 :r 0 :terrain-type :woods}
+     {:q 11 :r 0 :terrain-type :woods}
+     {:q 12 :r 0 :terrain-type :woods}
      ;; Row 2
-     {:q 1
-      :r 1
-      :terrain-type :woods}
-     {:q 2
-      :r 1
-      :terrain-type :plains}
-     {:q 3
-      :r 1
-      :terrain-type :plains}
-     {:q 4
-      :r 1
-      :terrain-type :plains}
-     {:q 5
-      :r 1
-      :terrain-type :plains}
-     {:q 6
-      :r 1
-      :terrain-type :woods}
-     {:q 8
-      :r 1
-      :terrain-type :woods}
-     {:q 9
-      :r 1
-      :terrain-type :plains}
-     {:q 10
-      :r 1
-      :terrain-type :plains}
-     {:q 11
-      :r 1
-      :terrain-type :plains}
-     {:q 12
-      :r 1
-      :terrain-type :woods}
-     {:q 13
-      :r 1
-      :terrain-type :woods}
+     {:q 1 :r 1 :terrain-type :woods}
+     {:q 2 :r 1 :terrain-type :plains}
+     {:q 3 :r 1 :terrain-type :plains}
+     {:q 4 :r 1 :terrain-type :plains}
+     {:q 5 :r 1 :terrain-type :plains}
+     {:q 6 :r 1 :terrain-type :woods}
+     {:q 8 :r 1 :terrain-type :woods}
+     {:q 9 :r 1 :terrain-type :plains}
+     {:q 10 :r 1 :terrain-type :plains}
+     {:q 11 :r 1 :terrain-type :plains}
+     {:q 12 :r 1 :terrain-type :woods}
+     {:q 13 :r 1 :terrain-type :woods}
      ;; Row 3
-     {:q 1
-      :r 2
-      :terrain-type :woods}
-     {:q 2
-      :r 2
-      :terrain-type :plains}
-     {:q 3
-      :r 2
-      :terrain-type :mountains}
-     ;; Multiplayer base location (unowned)
-     {:q 4
-      :r 2
-      :terrain-type :plains}
-     {:q 5
-      :r 2
-      :terrain-type :plains}
-     {:q 6
-      :r 2
-      :terrain-type :plains}
-     {:q 7
-      :r 2
-      :terrain-type :woods}
-     {:q 8
-      :r 2
-      :terrain-type :woods}
-     {:q 9
-      :r 2
-      :terrain-type :plains}
-     {:q 10
-      :r 2
-      :terrain-type :plains}
-     {:q 11
-      :r 2
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 12
-      :r 2
-      :terrain-type :plains}
-     {:q 13
-      :r 2
-      :terrain-type :plains}
-     {:q 14
-      :r 2
-      :terrain-type :woods}
+     {:q 1 :r 2 :terrain-type :woods}
+     {:q 2 :r 2 :terrain-type :plains}
+     {:q 3 :r 2 :terrain-type :mountains}
+     {:q 4 :r 2 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 5 :r 2 :terrain-type :plains}
+     {:q 6 :r 2 :terrain-type :plains}
+     {:q 7 :r 2 :terrain-type :woods}
+     {:q 8 :r 2 :terrain-type :woods}
+     {:q 9 :r 2 :terrain-type :plains}
+     {:q 10 :r 2 :terrain-type :plains}
+     {:q 11 :r 2 :terrain-type :plains}
+     {:q 12 :r 2 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 13 :r 2 :terrain-type :plains}
+     {:q 14 :r 2 :terrain-type :woods}
      ;; Row 4
-     {:q 0
-      :r 3
-      :terrain-type :plains}
-     {:q 1
-      :r 3
-      :terrain-type :plains}
-     {:q 2
-      :r 3
-      :terrain-type :mountains}
-     ;; Multiplayer base location (red owned)
-     {:q 3
-      :r 3
-      :terrain-type :plains}
-     {:q 4
-      :r 3
-      :terrain-type :mountains}
-     ;; Multiplayer base location (unowned)
-     {:q 5
-      :r 3
-      :terrain-type :plains}
-     {:q 6
-      :r 3
-      :terrain-type :woods}
-     {:q 7
-      :r 3
-      :terrain-type :mountains}
-     {:q 8
-      :r 3
-      :terrain-type :mountains}
-     {:q 9
-      :r 3
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 10
-      :r 3
-      :terrain-type :plains}
-     {:q 11
-      :r 3
-      :terrain-type :desert}
-     {:q 12
-      :r 3
-      :terrain-type :woods}
-     {:q 13
-      :r 3
-      :terrain-type :plains}
-     {:q 14
-      :r 3
-      :terrain-type :woods}
+     {:q 0 :r 3 :terrain-type :plains}
+     {:q 1 :r 3 :terrain-type :plains}
+     {:q 2 :r 3 :terrain-type :mountains}
+     {:q 3 :r 3 :terrain-type :plains} ;; Multiplayer base location (red owned)
+     {:q 4 :r 3 :terrain-type :mountains}
+     {:q 5 :r 3 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 6 :r 3 :terrain-type :woods}
+     {:q 7 :r 3 :terrain-type :mountains}
+     {:q 8 :r 3 :terrain-type :mountains}
+     {:q 9 :r 3 :terrain-type :plains}
+     {:q 10 :r 3 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 11 :r 3 :terrain-type :desert}
+     {:q 12 :r 3 :terrain-type :woods}
+     {:q 13 :r 3 :terrain-type :plains}
+     {:q 14 :r 3 :terrain-type :woods}
      ;; Row 5
-     {:q 0
-      :r 4
-      :terrain-type :woods}
-     {:q 1
-      :r 4
-      :terrain-type :plains}
-     ;; Multiplayer base location (red)
-     {:q 2
-      :r 4
-      :terrain-type :plains}
-     {:q 3
-      :r 4
-      :terrain-type :desert}
-     ;; Multiplayer base location (unknown)
-     {:q 4
-      :r 4
-      :terrain-type :plains}
-     {:q 5
-      :r 4
-      :terrain-type :desert}
-     {:q 6
-      :r 4
-      :terrain-type :plains}
-     {:q 7
-      :r 4
-      :terrain-type :woods}
-     {:q 8
-      :r 4
-      :terrain-type :mountains}
-     {:q 9
-      :r 4
-      :terrain-type :plains}
-     {:q 10
-      :r 4
-      :terrain-type :mountains}
-     {:q 11
-      :r 4
-      :terrain-type :desert}
-     ;; Multiplayer base location (unowned)
-     {:q 12
-      :r 4
-      :terrain-type :plains}
-     {:q 13
-      :r 4
-      :terrain-type :mountains}
-     {:q 14
-      :r 4
-      :terrain-type :plains}
+     {:q 0 :r 4 :terrain-type :woods}
+     {:q 1 :r 4 :terrain-type :plains}
+     {:q 2 :r 4 :terrain-type :plains} ;; Multiplayer base location (red)
+     {:q 3 :r 4 :terrain-type :desert}
+     {:q 4 :r 4 :terrain-type :plains} ;; Multiplayer base location (unknown)
+     {:q 5 :r 4 :terrain-type :desert}
+     {:q 6 :r 4 :terrain-type :plains}
+     {:q 7 :r 4 :terrain-type :woods}
+     {:q 8 :r 4 :terrain-type :mountains}
+     {:q 9 :r 4 :terrain-type :plains}
+     {:q 10 :r 4 :terrain-type :mountains}
+     {:q 11 :r 4 :terrain-type :desert}
+     {:q 12 :r 4 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 13 :r 4 :terrain-type :mountains}
+     {:q 14 :r 4 :terrain-type :plains}
      ;; Row 6
-     {:q 0
-      :r 5
-      :terrain-type :woods}
-     {:q 1
-      :r 5
-      :terrain-type :plains}
-     {:q 2
-      :r 5
-      :terrain-type :plains}
-     {:q 3
-      :r 5
-      :terrain-type :plains}
-     {:q 4
-      :r 5
-      :terrain-type :woods}
-     {:q 5
-      :r 5
-      :terrain-type :plains}
-     {:q 6
-      :r 5
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 7
-      :r 5
-      :terrain-type :plains}
-     {:q 8
-      :r 5
-      :terrain-type :plains}
-     {:q 9
-      :r 5
-      :terrain-type :plains}
-     {:q 10
-      :r 5
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 11
-      :r 5
-      :terrain-type :plains}
-     {:q 12
-      :r 5
-      :terrain-type :desert}
-     ;; Multiplayer base location (blue)
-     {:q 13
-      :r 5
-      :terrain-type :plains}
-     {:q 14
-      :r 5
-      :terrain-type :woods}
+     {:q 0 :r 5 :terrain-type :woods}
+     {:q 1 :r 5 :terrain-type :plains}
+     {:q 2 :r 5 :terrain-type :plains}
+     {:q 3 :r 5 :terrain-type :plains}
+     {:q 4 :r 5 :terrain-type :woods}
+     {:q 5 :r 5 :terrain-type :plains}
+     {:q 6 :r 5 :terrain-type :plains}
+     {:q 7 :r 5 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 8 :r 5 :terrain-type :plains}
+     {:q 9 :r 5 :terrain-type :plains}
+     {:q 10 :r 5 :terrain-type :plains}
+     {:q 11 :r 5 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 12 :r 5 :terrain-type :desert}
+     {:q 13 :r 5 :terrain-type :plains} ;; Multiplayer base location (blue)
+     {:q 14 :r 5 :terrain-type :woods}
      ;; Row 7
-     {:q 1
-      :r 6
-      :terrain-type :woods}
-     {:q 2
-      :r 6
-      :terrain-type :woods}
-     {:q 3
-      :r 6
-      :terrain-type :plains}
-     {:q 4
-      :r 6
-      :terrain-type :woods}
-     {:q 5
-      :r 6
-      :terrain-type :mountains}
-     {:q 6
-      :r 6
-      :terrain-type :plains}
-     {:q 7
-      :r 6
-      :terrain-type :plains}
-     {:q 8
-      :r 6
-      :terrain-type :woods}
-     {:q 9
-      :r 6
-      :terrain-type :plains}
-     {:q 10
-      :r 6
-      :terrain-type :mountains}
-     {:q 11
-      :r 6
-      :terrain-type :plains}
-     {:q 12
-      :r 6
-      :terrain-type :plains}
-     {:q 13
-      :r 6
-      :terrain-type :mountains}
-     {:q 14
-      :r 6
-      :terrain-type :plains}
+     {:q 1 :r 6 :terrain-type :woods}
+     {:q 2 :r 6 :terrain-type :woods}
+     {:q 3 :r 6 :terrain-type :plains}
+     {:q 4 :r 6 :terrain-type :woods}
+     {:q 5 :r 6 :terrain-type :mountains}
+     {:q 6 :r 6 :terrain-type :plains}
+     {:q 7 :r 6 :terrain-type :plains}
+     {:q 8 :r 6 :terrain-type :woods}
+     {:q 9 :r 6 :terrain-type :plains}
+     {:q 10 :r 6 :terrain-type :mountains}
+     {:q 11 :r 6 :terrain-type :plains}
+     {:q 12 :r 6 :terrain-type :plains}
+     {:q 13 :r 6 :terrain-type :mountains}
+     {:q 14 :r 6 :terrain-type :plains}
      ;; Row 8
-     {:q 1
-      :r 7
-      :terrain-type :woods}
-     {:q 2
-      :r 7
-      :terrain-type :woods}
-     {:q 3
-      :r 7
-      :terrain-type :plains}
-     {:q 4
-      :r 7
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 5
-      :r 7
-      :terrain-type :plains}
-     {:q 6
-      :r 7
-      :terrain-type :mountains}
-     ;; Multiplayer base location (unowned)
-     {:q 7
-      :r 7
-      :terrain-type :plains}
-     {:q 8
-      :r 7
-      :terrain-type :mountains}
-     ;; Multiplayer base location (unowned)
-     {:q 9
-      :r 7
-      :terrain-type :plains}
-     {:q 10
-      :r 7
-      :terrain-type :plains}
-     {:q 11
-      :r 7
-      :terrain-type :plains}
-     {:q 12
-      :r 7
-      :terrain-type :mountains}
-     {:q 13
-      :r 7
-      :terrain-type :plains}
-     {:q 14
-      :r 7
-      :terrain-type :woods}
+     {:q 1 :r 7 :terrain-type :woods}
+     {:q 2 :r 7 :terrain-type :woods}
+     {:q 3 :r 7 :terrain-type :plains}
+     {:q 4 :r 7 :terrain-type :plains}
+     {:q 5 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 6 :r 7 :terrain-type :mountains}
+     {:q 7 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 8 :r 7 :terrain-type :mountains}
+     {:q 9 :r 7 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 10 :r 7 :terrain-type :plains}
+     {:q 11 :r 7 :terrain-type :plains}
+     {:q 12 :r 7 :terrain-type :mountains}
+     {:q 13 :r 7 :terrain-type :plains}
+     {:q 14 :r 7 :terrain-type :woods}
      ;; Row 9
-     {:q 1
-      :r 8
-      :terrain-type :woods}
-     {:q 2
-      :r 8
-      :terrain-type :woods}
-     {:q 3
-      :r 8
-      :terrain-type :plains}
-     {:q 4
-      :r 8
-      :terrain-type :mountains}
-     {:q 5
-      :r 8
-      :terrain-type :plains}
-     {:q 6
-      :r 8
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 7
-      :r 8
-      :terrain-type :plains}
-     {:q 8
-      :r 8
-      :terrain-type :plains}
-     {:q 9
-      :r 8
-      :terrain-type :plains}
-     {:q 10
-      :r 8
-      :terrain-type :mountains}
-     {:q 11
-      :r 8
-      :terrain-type :plains}
-     {:q 12
-      :r 8
-      :terrain-type :plains}
-     {:q 13
-      :r 8
-      :terrain-type :plains}
-     {:q 14
-      :r 8
-      :terrain-type :woods}
+     {:q 1 :r 8 :terrain-type :woods}
+     {:q 2 :r 8 :terrain-type :woods}
+     {:q 3 :r 8 :terrain-type :plains}
+     {:q 4 :r 8 :terrain-type :mountains}
+     {:q 5 :r 8 :terrain-type :plains}
+     {:q 6 :r 8 :terrain-type :plains}
+     {:q 7 :r 8 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 8 :r 8 :terrain-type :plains}
+     {:q 9 :r 8 :terrain-type :plains}
+     {:q 10 :r 8 :terrain-type :mountains}
+     {:q 11 :r 8 :terrain-type :plains}
+     {:q 12 :r 8 :terrain-type :plains}
+     {:q 13 :r 8 :terrain-type :plains}
+     {:q 14 :r 8 :terrain-type :woods}
      ;; Row 10
-     {:q 0
-      :r 9
-      :terrain-type :woods}
-     {:q 1
-      :r 9
-      :terrain-type :plains}
-     {:q 2
-      :r 9
-      :terrain-type :plains}
-     {:q 3
-      :r 9
-      :terrain-type :desert}
-     ;; Multiplayer base location (unowned)
-     {:q 4
-      :r 9
-      :terrain-type :woods}
-     {:q 5
-      :r 9
-      :terrain-type :plains}
-     {:q 6
-      :r 9
-      :terrain-type :plains}
-     {:q 7
-      :r 9
-      :terrain-type :mountains}
-     {:q 8
-      :r 9
-      :terrain-type :plains}
-     {:q 9
-      :r 9
-      :terrain-type :desert}
-     {:q 10
-      :r 9
-      :terrain-type :woods}
-     {:q 11
-      :r 9
-      :terrain-type :woods}
-     ;; Multiplayer base location (unowned)
-     {:q 12
-      :r 9
-      :terrain-type :plains}
-     {:q 13
-      :r 9
-      :terrain-type :plains}
-     {:q 14
-      :r 9
-      :terrain-type :woods}
+     {:q 0 :r 9 :terrain-type :woods}
+     {:q 1 :r 9 :terrain-type :plains}
+     {:q 2 :r 9 :terrain-type :plains}
+     {:q 3 :r 9 :terrain-type :desert}
+     {:q 4 :r 9 :terrain-type :woods} ;; Multiplayer base location (unowned)
+     {:q 5 :r 9 :terrain-type :plains}
+     {:q 6 :r 9 :terrain-type :plains}
+     {:q 7 :r 9 :terrain-type :mountains}
+     {:q 8 :r 9 :terrain-type :plains}
+     {:q 9 :r 9 :terrain-type :desert}
+     {:q 10 :r 9 :terrain-type :woods}
+     {:q 11 :r 9 :terrain-type :woods}
+     {:q 12 :r 9 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 13 :r 9 :terrain-type :plains}
+     {:q 14 :r 9 :terrain-type :woods}
      ;; Row 11
-     {:q 0
-      :r 10
-      :terrain-type :woods}
-     {:q 1
-      :r 10
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 2
-      :r 10
-      :terrain-type :plains}
-     {:q 3
-      :r 10
-      :terrain-type :desert}
-     {:q 4
-      :r 10
-      :terrain-type :desert}
-     {:q 5
-      :r 10
-      :terrain-type :plains}
-     {:q 6
-      :r 10
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 7
-      :r 10
-      :terrain-type :plains}
-     {:q 8
-      :r 10
-      :terrain-type :woods}
-     {:q 9
-      :r 10
-      :terrain-type :plains}
-     {:q 10
-      :r 10
-      :terrain-type :desert}
-     ;; Multiplayer base location (unowned)
-     {:q 11
-      :r 10
-      :terrain-type :plains}
-     {:q 12
-      :r 10
-      :terrain-type :desert}
-     {:q 13
-      :r 10
-      :terrain-type :plains}
-     {:q 14
-      :r 10
-      :terrain-type :plains}
+     {:q 0 :r 10 :terrain-type :woods}
+     {:q 1 :r 10 :terrain-type :plains}
+     {:q 2 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 3 :r 10 :terrain-type :desert}
+     {:q 4 :r 10 :terrain-type :desert}
+     {:q 5 :r 10 :terrain-type :plains}
+     {:q 6 :r 10 :terrain-type :plains}
+     {:q 7 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 8 :r 10 :terrain-type :woods}
+     {:q 9 :r 10 :terrain-type :plains}
+     {:q 10 :r 10 :terrain-type :desert}
+     {:q 11 :r 10 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 12 :r 10 :terrain-type :desert}
+     {:q 13 :r 10 :terrain-type :plains}
+     {:q 14 :r 10 :terrain-type :plains}
      ;; Row 12
-     {:q 0
-      :r 11
-      :terrain-type :plains}
-     {:q 1
-      :r 11
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 2
-      :r 11
-      :terrain-type :plains}
-     ;; Multiplayer base location (yellow)
-     {:q 3
-      :r 11
-      :terrain-type :plains}
-     ;; Multiplayer base location (unowned)
-     {:q 4
-      :r 11
-      :terrain-type :plains}
-     {:q 5
-      :r 11
-      :terrain-type :plains}
-     {:q 6
-      :r 11
-      :terrain-type :mountains}
-     {:q 7
-      :r 11
-      :terrain-type :mountains}
-     {:q 8
-      :r 11
-      :terrain-type :woods}
-     ;; Multiplayer base location (unowned)
-     {:q 9
-      :r 11
-      :terrain-type :plains}
-     {:q 10
-      :r 11
-      :terrain-type :mountains}
-     ;; Multiplayer base location (pink)
-     {:q 11
-      :r 11
-      :terrain-type :plains}
-     {:q 12
-      :r 11
-      :terrain-type :plains}
-     {:q 13
-      :r 11
-      :terrain-type :plains}
-     {:q 14
-      :r 11
-      :terrain-type :woods}
+     {:q 0 :r 11 :terrain-type :plains}
+     {:q 1 :r 11 :terrain-type :plains}
+     {:q 2 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 3 :r 11 :terrain-type :plains} ;; Multiplayer base location (yellow)
+     {:q 4 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 5 :r 11 :terrain-type :plains}
+     {:q 6 :r 11 :terrain-type :mountains}
+     {:q 7 :r 11 :terrain-type :mountains}
+     {:q 8 :r 11 :terrain-type :woods}
+     {:q 9 :r 11 :terrain-type :plains} ;; Multiplayer base location (unowned)
+     {:q 10 :r 11 :terrain-type :mountains}
+     {:q 11 :r 11 :terrain-type :plains} ;; Multiplayer base location (pink)
+     {:q 12 :r 11 :terrain-type :plains}
+     {:q 13 :r 11 :terrain-type :plains}
+     {:q 14 :r 11 :terrain-type :woods}
      ;; Row 13
-     {:q 0
-      :r 12
-      :terrain-type :woods}
-     {:q 1
-      :r 12
-      :terrain-type :woods}
-     {:q 2
-      :r 12
-      :terrain-type :woods}
-     {:q 3
-      :r 12
-      :terrain-type :mountains}
-     {:q 4
-      :r 12
-      :terrain-type :desert}
-     {:q 5
-      :r 12
-      :terrain-type :mountains}
-     {:q 6
-      :r 12
-      :terrain-type :plains}
-     {:q 7
-      :r 12
-      :terrain-type :plains}
-     {:q 8
-      :r 12
-      :terrain-type :plains}
-     {:q 9
-      :r 12
-      :terrain-type :plains}
-     {:q 10
-      :r 12
-      :terrain-type :plains}
-     {:q 11
-      :r 12
-      :terrain-type :desert}
-     {:q 12
-      :r 12
-      :terrain-type :plains}
-     {:q 13
-      :r 12
-      :terrain-type :woods}
-     {:q 14
-      :r 12
-      :terrain-type :woods}
+     {:q 0 :r 12 :terrain-type :woods}
+     {:q 1 :r 12 :terrain-type :woods}
+     {:q 2 :r 12 :terrain-type :woods}
+     {:q 3 :r 12 :terrain-type :mountains}
+     {:q 4 :r 12 :terrain-type :desert}
+     {:q 5 :r 12 :terrain-type :mountains}
+     {:q 6 :r 12 :terrain-type :plains}
+     {:q 7 :r 12 :terrain-type :plains}
+     {:q 8 :r 12 :terrain-type :plains}
+     {:q 9 :r 12 :terrain-type :plains}
+     {:q 10 :r 12 :terrain-type :plains}
+     {:q 11 :r 12 :terrain-type :desert}
+     {:q 12 :r 12 :terrain-type :plains}
+     {:q 13 :r 12 :terrain-type :woods}
+     {:q 14 :r 12 :terrain-type :woods}
      ;; Row 14
-     {:q 1
-      :r 13
-      :terrain-type :woods}
-     {:q 2
-      :r 13
-      :terrain-type :plains}
-     ;; Multiplayer base location (yellow)
-     {:q 3
-      :r 13
-      :terrain-type :plains}
-     {:q 4
-      :r 13
-      :terrain-type :plains}
-     {:q 5
-      :r 13
-      :terrain-type :woods}
-     {:q 6
-      :r 13
-      :terrain-type :woods}
-     {:q 7
-      :r 13
-      :terrain-type :woods}
-     {:q 8
-      :r 13
-      :terrain-type :woods}
-     {:q 9
-      :r 13
-      :terrain-type :plains}
-     ;; Multiplayer base location (pink)
-     {:q 10
-      :r 13
-      :terrain-type :plains}
-     {:q 11
-      :r 13
-      :terrain-type :plains}
-     {:q 12
-      :r 13
-      :terrain-type :woods}
+     {:q 1 :r 13 :terrain-type :woods}
+     {:q 2 :r 13 :terrain-type :plains}
+     {:q 3 :r 13 :terrain-type :plains} ;; Multiplayer base location (yellow)
+     {:q 4 :r 13 :terrain-type :plains}
+     {:q 5 :r 13 :terrain-type :woods}
+     {:q 6 :r 13 :terrain-type :woods}
+     {:q 7 :r 13 :terrain-type :woods}
+     {:q 8 :r 13 :terrain-type :woods}
+     {:q 9 :r 13 :terrain-type :plains}
+     {:q 10 :r 13 :terrain-type :plains} ;; Multiplayer base location (pink)
+     {:q 11 :r 13 :terrain-type :plains}
+     {:q 12 :r 13 :terrain-type :woods}
      ;; Row 15
-     {:q 2
-      :r 14
-      :terrain-type :woods}
-     {:q 3
-      :r 14
-      :terrain-type :plains}
-     {:q 4
-      :r 14
-      :terrain-type :plains}
-     {:q 5
-      :r 14
-      :terrain-type :woods}
-     {:q 6
-      :r 14
-      :terrain-type :woods}
-     {:q 8
-      :r 14
-      :terrain-type :woods}
-     {:q 9
-      :r 14
-      :terrain-type :woods}
-     {:q 10
-      :r 14
-      :terrain-type :plains}
-     {:q 11
-      :r 14
-      :terrain-type :plains}
-     {:q 12
-      :r 14
-      :terrain-type :woods}
-     ]
-    }
-   })
+     {:q 2 :r 14 :terrain-type :woods}
+     {:q 3 :r 14 :terrain-type :plains}
+     {:q 4 :r 14 :terrain-type :plains}
+     {:q 5 :r 14 :terrain-type :woods}
+     {:q 6 :r 14 :terrain-type :woods}
+     {:q 8 :r 14 :terrain-type :woods}
+     {:q 9 :r 14 :terrain-type :woods}
+     {:q 10 :r 14 :terrain-type :plains}
+     {:q 11 :r 14 :terrain-type :plains}
+     {:q 12 :r 14 :terrain-type :woods}]}})

 ;; TODO: add support for :allowed-unit-types
 (def scenarios
@@ -1560,19 +969,13 @@
       :credits 300
       :ai false
       :bases [{:q 1 :r 2}]
-      :units [{:q 2
-               :r 2
-               :unit-type :infantry}]}
+      :units [{:q 2 :r 2 :unit-type :infantry}]}
      {:color :red
       :credits 300
       :ai true
       :bases [{:q 7 :r 6}]
-      :units [{:q 7
-               :r 7
-               :unit-type :infantry}
-              {:q 7
-               :r 8
-               :unit-type :infantry}]}]}
+      :units [{:q 7 :r 7 :unit-type :infantry}
+              {:q 7 :r 8 :unit-type :infantry}]}]}

    :city-sprawl-multiplayer
    {:id :city-sprawl-multiplayer
@@ -1615,34 +1018,26 @@
       :ai false
       :bases [{:q 3  :r 3}
               {:q 2  :r 4}]
-      :units [{:q 3 :r 2
-               :unit-type :infantry}
-              {:q 2 :r 3
-               :unit-type :infantry}]}
+      :units [{:q 3 :r 2 :unit-type :infantry}
+              {:q 2 :r 3 :unit-type :infantry}]}
      {:color :blue
       :credits 300
       :ai true
       :bases [{:q 12 :r 4}
               {:q 13 :r 5}]
-      :units [{:q 12 :r 3
-               :unit-type :infantry}
-              {:q 13 :r 4
-               :unit-type :infantry}]}
+      :units [{:q 12 :r 3 :unit-type :infantry}
+              {:q 13 :r 4 :unit-type :infantry}]}
      {:color :yellow
       :credits 300
       :ai true
       :bases [{:q 3 :r 11}
               {:q 3 :r 13}]
-      :units [{:q 2 :r 12
-               :unit-type :infantry}
-              {:q 3 :r 12
-               :unit-type :infantry}]}
+      :units [{:q 2 :r 12 :unit-type :infantry}
+              {:q 3 :r 12 :unit-type :infantry}]}
      {:color :pink
       :credits 300
       :ai true
       :bases [{:q 11 :r 11}
               {:q 10 :r 13}]
-      :units [{:q 10 :r 11
-               :unit-type :infantry}
-              {:q 11 :r 12
-               :unit-type :infantry}]}]}})
+      :units [{:q 10 :r 11 :unit-type :infantry}
+              {:q 11 :r 12 :unit-type :infantry}]}]}})


Add naval data to units' attack-strengths

--- src/cljs/zetawar/data.cljs
+++ src/cljs/zetawar/data.cljs
@@ -167,7 +167,8 @@
                  :seaport   {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}
                 :attack-strengths
                 {:personnel 6
-                 :armored   3}
+                 :armored   3
+                 :naval     4}
                 :zoc
                 [:personnel :armored]}
      :grenadier {:description "Grenadier"
@@ -195,7 +196,8 @@
                   :seaport   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}}
                  :attack-strengths
                  {:personnel 8
-                  :armored   9}
+                  :armored   9
+                  :naval     6}
                  :zoc
                  [:personnel :armored]}
      :mortar {:description "Mortar"
@@ -223,7 +225,8 @@
                :seaport   {:movement-cost 3 :armor-bonus  0 :attack-bonus  0}}
               :attack-strengths
               {:personnel 10
-               :armored   10}}
+               :armored   10
+               :naval     10}}
      :ranger {:description "Ranger"
               :cost 200
               :movement 9
@@ -250,7 +253,8 @@
                :seaport       {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}
               :attack-strengths
               {:personnel 9
-               :armored   4}
+               :armored   4
+               :naval     6}
               :zoc
               [:personnel :armored]}
      :medic {:description "Medic"
@@ -278,7 +282,8 @@
               :seaport   {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}
             :attack-strengths
             {:personnel 5
-             :armored   2}
+             :armored   2
+             :naval     3}
             :zoc
             [:personnel :armored]}
     :engineer {:description "Engineer"
@@ -307,7 +312,8 @@
                 :seaport       {:movement-cost 2 :armor-bonus  0 :attack-bonus  0}}
               :attack-strengths
               {:personnel 5
-               :armored   2}
+               :armored   2
+               :naval     3}
               :zoc
               [:personnel :armored]}
      ;; Armored
@@ -334,7 +340,8 @@
                :seaport {:movement-cost 2  :armor-bonus  0 :attack-bonus  0}}
               :attack-strengths
               {:personnel 9
-               :armored   3}
+               :armored   3
+               :naval     6}
               :zoc
               [:personnel :armored]}
      :tank {:description "Tank"
@@ -360,7 +367,8 @@
              :seaport {:movement-cost 2 :armor-bonus  0 :attack-bonus 0}}
             :attack-strengths
             {:personnel 10
-             :armored   10}
+             :armored   10
+             :naval     9}
             :zoc
             [:personnel :armored]}
      ;; Naval
@@ -382,7 +390,8 @@
                 :seaport       {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}
                :attack-strengths
                {:personnel 9
-                :armored   9}
+                :armored   9
+                :naval     8}
                :zoc
                [:personnel :armored]}
      :destroyer {:description "Destroyer"
@@ -402,7 +411,8 @@
                   :seaport {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}
                  :attack-strengths
                  {:personnel 10
-                  :armored   12}
+                  :armored   12
+                  :naval     8}
                  :zoc
                  [:personnel :armored]}
      :cruiser {:description "Cruiser"
@@ -421,7 +431,8 @@
                 :seaport {:movement-cost 1 :armor-bonus 0 :attack-bonus 0}}
                :attack-strengths
                {:personnel 14
-                :armored   16}
+                :armored   16
+                :naval     12}
                :zoc
                [:personnel :armored]}}}})


added ipad breakpoints

added ipad breakpoints

--- src/cljx/mesh/respond.cljx
+++ src/cljx/mesh/respond.cljx
@@ -46,22 +46,22 @@
   {:screen true
    :min-width (px 1824)})

-;; IPhones
+;; IPhones (3, 4, 4s, 5, 5s, 6)

-(def iphone-4-media-params
+(def iphone-3-4-media-params
   {:screen true
    :min-device-width (px 320)
    :max-device-width (px 480)
    :-webkit-min-device-pixel-ratio 2})

-(defbreakpoint iphone-4
-  iphone-4-media-params)
+(defbreakpoint iphone-3-4
+  iphone-3-4-media-params)

-(defbreakpoint iphone-4-landscape
-  (assoc iphone-4-media-params :orientation :landscape))
+(defbreakpoint iphone-3-4-landscape
+  (assoc iphone-3-4-media-params :orientation :landscape))

-(defbreakpoint iphone-4-portrait
-  (assoc iphone-4-media-params :orientation :portrait))
+(defbreakpoint iphone-3-4-portrait
+  (assoc iphone-3-4-media-params :orientation :portrait))

 (def iphone-5-media-params
   {:screen true
@@ -93,3 +93,38 @@
 (defbreakpoint iphone-6-portrait
   (assoc iphone-6-media-params :orientation :portrait))

+;; IPads
+
+(def ipad-1-2-media-params
+  {:screen true
+   :min-device-width (px 768)
+   :max-device-width (px 1024)
+   :-webkit-min-device-pixel-ratio 1})
+
+(defbreakpoint ipad-1-2
+  ipad-1-2-media-params)
+
+(defbreakpoint ipad-1-2-landscape
+  (assoc ipad-1-2-media-params :orientation :landscape))
+
+(defbreakpoint ipad-1-2-portrait
+  (assoc ipad-1-2-media-params :orientation :portrait))
+
+(def ipad-mini ipad-1-2)
+(def ipad-mini-landscape ipad-1-2-landscape)
+(def ipad-mini-portrait ipad-1-2-portrait)
+
+(def ipad-3-4-media-params
+  {:screen true
+   :min-device-width (px 768)
+   :max-device-width (px 1024)
+   :-webkit-min-device-pixel-ratio 2})
+
+(defbreakpoint ipad-3-4
+  ipad-3-4-media-params)
+
+(defbreakpoint ipad-3-4-landscape
+  (assoc ipad-3-4-media-params :orientation :landscape))
+
+(defbreakpoint ipad-3-4-portrait
+  (assoc ipad-3-4-media-params :orientation :portrait))


Add directory-listing function.

Add directory-listing function.

--- src/clojure/parkour/fs.clj
+++ src/clojure/parkour/fs.clj
@@ -74,3 +74,8 @@
   ([fs p]
      (->> (.globStatus ^FileSystem fs (path p))
           (map #(.getPath ^FileStatus %)))))
+
+(defn path-list
+  "List the entries in the directory at path `p`."
+  ([p] (path-list (path-fs p) p))
+  ([fs p] (map #(.getPath ^FileStatus %) (.listStatus fs (path p)))))


:bug: Fix reactivity issue on workspace and file name change.

--- frontend/src/uxbox/main/data/workspace.cljs
+++ frontend/src/uxbox/main/data/workspace.cljs
@@ -69,7 +69,6 @@

 ;; --- Initialize WebSocket

-
 (s/def ::type keyword?)
 (s/def ::message
   (s/keys :req-un [::type]))
@@ -189,8 +188,9 @@
    :drawing-tool nil
    :tooltip nil})

-(declare initialized)
+(declare initialize-state)
 (declare initialize-page)
+(declare initialize-file)

 (defn initialize
   "Initialize the workspace state."
@@ -210,35 +210,32 @@
                         (rx/filter (ptk/type? ::dp/files-fetched) stream))
                 (rx/take 1)
                 (rx/do #(reset! st/loader false))
-                (rx/mapcat #(rx/of (initialized file-id)
+                (rx/mapcat #(rx/of (initialize-state file-id)
+                                   (initialize-file file-id)
                                    (initialize-page page-id)
                                    #_(initialize-alignment page-id)))))
-          (rx/merge
-           (rx/of (initialize-page page-id))))))))
-
-(defn- initialized
+          (rx/of (initialize-file file-id)
+                 (initialize-page page-id)))))))
+
+(defn- initialize-state
   [file-id]
   (us/assert ::us/uuid file-id)
-  (ptk/reify ::initialized
-    ptk/UpdateEvent
-    (update [_ state]
-      (let [file (get-in state [:files file-id])
-            local (assoc workspace-default :file-id file-id)]
+  (ptk/reify ::initialize-state
+    ptk/UpdateEvent
+    (update [_ state]
+      (let [local (assoc workspace-default :file-id file-id)]
         (-> state
-            (assoc :workspace-file file)
             (assoc :workspace-layout default-layout)
             (assoc :workspace-local local))))))

-(defn finalize
-  [file-id page-id]
+(defn- initialize-file
+  [file-id]
   (us/assert ::us/uuid file-id)
-  (us/assert ::us/uuid page-id)
-  (ptk/reify ::finalize
-    ptk/UpdateEvent
-    (update [_ state]
-      (dissoc state
-              :workspace-page
-              :workspace-data))))
+  (ptk/reify ::initialize-file
+    ptk/UpdateEvent
+    (update [_ state]
+      (let [file (get-in state [:files file-id])]
+        (assoc state :workspace-file file)))))

 (defn initialize-page
   [page-id]
@@ -260,8 +257,18 @@
              (rx/filter #(satisfies? IBatchedChange %))
              (rx/debounce 500)
              (rx/map (constantly commit-batched-changes))
-             (rx/finalize #(prn "FINALIZE" %))
              (rx/take-until stoper))))))
+
+(defn finalize
+  [file-id page-id]
+  (us/assert ::us/uuid file-id)
+  (us/assert ::us/uuid page-id)
+  (ptk/reify ::finalize
+    ptk/UpdateEvent
+    (update [_ state]
+      (dissoc state
+              :workspace-page
+              :workspace-data))))

 ;; --- Fetch Workspace Users