|  | @@ -5,18 +5,21 @@
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  ; to generate random values
 | 
	
		
			
				|  |  |  ;for(let i = 0, s = new Set(); i < 10; i++){ let r = Math.floor(Math.random() * 15)+1, c = a[Math.floor(Math.random() * a.length)], k = `${c}${r}`; if(s.has(k)){ i--; continue; } s.add(k); v.push(`{:row ${r} :col "${c}" :value "${Math.floor(Math.random() * 10000)}"}`); }
 | 
	
		
			
				|  |  | -(def sample-data [{:row 1 :col "A" :value "59"}
 | 
	
		
			
				|  |  | -                  {:row 5 :col "C" :value "269"}
 | 
	
		
			
				|  |  | -                  {:row 4 :col "B" :value "7893"}
 | 
	
		
			
				|  |  | -                  {:row 2 :col "F" :value "8650"}
 | 
	
		
			
				|  |  | -                  {:row 6 :col "D" :value "4065"}
 | 
	
		
			
				|  |  | -                  {:row 7 :col "F" :value "5316"}
 | 
	
		
			
				|  |  | -                  {:row 12 :col "A" :value "2405"}
 | 
	
		
			
				|  |  | -                  {:row 5 :col "B" :value "7863"}
 | 
	
		
			
				|  |  | -                  {:row 9 :col "E" :value "3144"}
 | 
	
		
			
				|  |  | -                  {:row 10 :col "D" :value "8272"}
 | 
	
		
			
				|  |  | -                  {:row 11 :col "D" :value "2495"}
 | 
	
		
			
				|  |  | -                  {:row 15 :col "E" :value "8968"}])
 | 
	
		
			
				|  |  | +(def sample-data [{:row 1 :col "A" :value "59" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 5 :col "C" :value "269" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 4 :col "B" :value "7893" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 2 :col "F" :value "8650" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 6 :col "D" :value "4065" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 7 :col "F" :value "5316" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 12 :col "A" :value "2405" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 5 :col "B" :value "7863" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 9 :col "E" :value "3144" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 10 :col "D" :value "8272" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 11 :col "D" :value "2495" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 15 :col "E" :value "8968" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 7 :col "B" :value "=C5 + D6" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 8 :col "B" :value "=B7 * 2" :view :display}
 | 
	
		
			
				|  |  | +                  {:row 7 :col "C" :value "=D1" :view :display}])
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  (defonce data-atom (r/atom sample-data))
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -39,38 +42,14 @@
 | 
	
		
			
				|  |  |    (if (nil? datum)
 | 
	
		
			
				|  |  |      (swap! data-atom conj {:row r :col c :value value})
 | 
	
		
			
				|  |  |      (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :value value) %) d)))))
 | 
	
		
			
				|  |  | +(defn update-display [c r display]
 | 
	
		
			
				|  |  | +  (println (str "trying to update " c r " to " display))
 | 
	
		
			
				|  |  | +  (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :display display) %) d))))
 | 
	
		
			
				|  |  | +(defn toggle-display [c r view-mode]
 | 
	
		
			
				|  |  | +  (println (str "toggling " c r " to " view-mode))
 | 
	
		
			
				|  |  | +  (swap! data-atom (fn [d] (map #(if (and (= r (:row %)) (= c (:col %))) (assoc % :view view-mode) %) d))))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -;; -------------------------
 | 
	
		
			
				|  |  | -;; Views
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -(defn cell [c r data]
 | 
	
		
			
				|  |  | -  (let [datum (some #(if (and (= c (:col %)) (= r (:row %))) %) data)]
 | 
	
		
			
				|  |  | -    ^{:key (str c r)} [:td [:input {:value (or (:value datum) "")
 | 
	
		
			
				|  |  | -                                    :on-change #(update-value c r datum (.. % -target -value))
 | 
	
		
			
				|  |  | -                                    :on-blur (fn [e] (println (str "blur! " c r)))
 | 
	
		
			
				|  |  | -                                    :on-focus (fn [e] (println (str "focus! " c r)))}]]
 | 
	
		
			
				|  |  | -    )
 | 
	
		
			
				|  |  | -  )
 | 
	
		
			
				|  |  | -(defn row [r cols data]
 | 
	
		
			
				|  |  | -  ^{:key (str "row-" r)} [:tr
 | 
	
		
			
				|  |  | -                          (cons
 | 
	
		
			
				|  |  | -                            ^{:key (str "row-head-" r)} [:th (str r)]
 | 
	
		
			
				|  |  | -                            (map #(cell % r data) cols))])
 | 
	
		
			
				|  |  | -(defn header-row [cols]
 | 
	
		
			
				|  |  | -  ^{:key "header"} [:tr
 | 
	
		
			
				|  |  | -   (cons
 | 
	
		
			
				|  |  | -     ^{:key "corner"} [:th]
 | 
	
		
			
				|  |  | -     (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))])
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -(defn sheet [data]
 | 
	
		
			
				|  |  | -  [:table [:tbody
 | 
	
		
			
				|  |  | -           (let [maxrow (highest :row data)
 | 
	
		
			
				|  |  | -                 cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
 | 
	
		
			
				|  |  | -             (cons
 | 
	
		
			
				|  |  | -               (header-row cols)
 | 
	
		
			
				|  |  | -               (map #(row % cols data) (range 1 (inc maxrow)))))
 | 
	
		
			
				|  |  | -           ]])
 | 
	
		
			
				|  |  | +; CALCULATION / FORMULA EVALUATION FUNCTIONS
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  (defn temp1 [data]
 | 
	
		
			
				|  |  |    (let [first-order (group-by #(not= (first (:value %)) "=") data)
 | 
	
	
		
			
				|  | @@ -81,11 +60,22 @@
 | 
	
		
			
				|  |  |      {:remaining (zipmap non-evaluated-cells non-evaluated-values)
 | 
	
		
			
				|  |  |       :evaluated (zipmap evaluated-cells evaluated-values)}))
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +(def parse-variables (memoize (fn [expression]
 | 
	
		
			
				|  |  | +                                (js->clj (.getVariables mathjs expression)))))
 | 
	
		
			
				|  |  | +(defn temp3 [[k v]]
 | 
	
		
			
				|  |  | +  (let [w (name k)
 | 
	
		
			
				|  |  | +        c (re-find #"^[A-Z]+" w)
 | 
	
		
			
				|  |  | +        r (.parseInt js/window (re-find #"[0-9]+$" w))]
 | 
	
		
			
				|  |  | +    (do
 | 
	
		
			
				|  |  | +      (update-display c r v)
 | 
	
		
			
				|  |  | +      )
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  )
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  (defn temp2 [evaluated remaining]
 | 
	
		
			
				|  |  |    (if (empty? remaining)
 | 
	
		
			
				|  |  |      evaluated
 | 
	
		
			
				|  |  | -    (let [jsvars (map #(.getVariables mathjs (val %)) remaining)
 | 
	
		
			
				|  |  | -          vars (js->clj jsvars)
 | 
	
		
			
				|  |  | +    (let [vars (map #(parse-variables (val %)) remaining)
 | 
	
		
			
				|  |  |            remaining-vars (zipmap (keys remaining) vars)
 | 
	
		
			
				|  |  |            evaluated-vars (map (fn [x] (map #(let [item (keyword %)]
 | 
	
		
			
				|  |  |                                                (cond
 | 
	
	
		
			
				|  | @@ -96,24 +86,60 @@
 | 
	
		
			
				|  |  |            parsed (zipmap (keys remaining) evaluated-vars)
 | 
	
		
			
				|  |  |            ready-or-not (group-by (fn [[ k v ]] (some #(not= :not-yet (last %)) v)) parsed)
 | 
	
		
			
				|  |  |            ;TODO: detect circular references
 | 
	
		
			
				|  |  | -          ;prepared (map #([(key %) (.stringify js/JSON (apply js-obj (flatten (val %))))]) (ready-or-not true))
 | 
	
		
			
				|  |  | -          ;prepared (map (fn [[k v]] [k (.evaluate mathjs (remaining k) (.stringify js/JSON (apply js-obj (flatten v)))) (.stringify js/JSON (apply js-obj (flatten v)))]) (ready-or-not true))
 | 
	
		
			
				|  |  |            prepared (map (fn [[k v]] [k (.evaluate mathjs (remaining k) (apply js-obj (flatten v)))]) (ready-or-not true))
 | 
	
		
			
				|  |  | -          ;TODO: merge new values into data with the :display keyword (or similar)
 | 
	
		
			
				|  |  | +          new-evaluated (reduce conj evaluated prepared)
 | 
	
		
			
				|  |  | +          still-remaining (apply hash-map (flatten (map #(list (key %) (remaining (key %))) (ready-or-not nil))))
 | 
	
		
			
				|  |  | +          ;merged (map temp3 prepared) ;TODO: only merge after recursion is complete (outside this function)
 | 
	
		
			
				|  |  |            ]
 | 
	
		
			
				|  |  |        ;remaining ; {:B8 B7 * 2, :C7 D1, :B7 C5 + D6}
 | 
	
		
			
				|  |  | +      ;vars ; ([B7] [D1] [C5 D6])
 | 
	
		
			
				|  |  |        ;remaining-vars ; {:B8 [B7], :C7 [D1], :B7 [C5 D6]}
 | 
	
		
			
				|  |  |        ;parsed ; {:B8 ([B7 :not-yet]), :C7 ([D1 0]), :B7 ([C5 269] [D6 4065])}
 | 
	
		
			
				|  |  |        ;ready-or-not ; {nil [[:B8 ([B7 :not-yet])]], true [[:C7 ([D1 0])] [:B7 ([C5 269] [D6 4065])]]}
 | 
	
		
			
				|  |  | -      prepared ; ([:C7 0] [:B7 4334])
 | 
	
		
			
				|  |  | +      ;prepared ; ([:C7 0] [:B7 4334])
 | 
	
		
			
				|  |  | +      ;still-remaining ; {:B8 B7 * 2}
 | 
	
		
			
				|  |  | +      ;evaluated; TODO: change name, consider putting "data" directly in here
 | 
	
		
			
				|  |  | +      (recur new-evaluated still-remaining)
 | 
	
		
			
				|  |  | +      ;new-evaluated
 | 
	
		
			
				|  |  |        )
 | 
	
		
			
				|  |  |      )
 | 
	
		
			
				|  |  |    )
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +;; -------------------------
 | 
	
		
			
				|  |  | +;; Views
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defn cell [c r data]
 | 
	
		
			
				|  |  | +  (let [datum (some #(if (and (= c (:col %)) (= r (:row %))) %) data)]
 | 
	
		
			
				|  |  | +    ^{:key (str c r)} [:td [:input {:value (if (= (get datum :view nil) :value) (get datum :value "") (get datum :display (get datum :value "")))
 | 
	
		
			
				|  |  | +                                    :on-change #(update-value c r datum (.. % -target -value))
 | 
	
		
			
				|  |  | +                                    :on-blur (fn [e] (println (str "blur! " c r)) (toggle-display c r :display))
 | 
	
		
			
				|  |  | +                                    :on-focus (fn [e] (println (str "focus! " c r)) (toggle-display c r :value))}]]
 | 
	
		
			
				|  |  | +    )
 | 
	
		
			
				|  |  | +  )
 | 
	
		
			
				|  |  | +(defn row [r cols data]
 | 
	
		
			
				|  |  | +  ^{:key (str "row-" r)} [:tr
 | 
	
		
			
				|  |  | +                          (cons
 | 
	
		
			
				|  |  | +                            ^{:key (str "row-head-" r)} [:th (str r)]
 | 
	
		
			
				|  |  | +                            (map #(cell % r data) cols))])
 | 
	
		
			
				|  |  | +(defn header-row [cols]
 | 
	
		
			
				|  |  | +  ^{:key "header"} [:tr
 | 
	
		
			
				|  |  | +   (cons
 | 
	
		
			
				|  |  | +     ^{:key "corner"} [:th]
 | 
	
		
			
				|  |  | +     (map (fn [c] ^{:key (str "col-head-" c)} [:th c]) cols))])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +(defn sheet [data]
 | 
	
		
			
				|  |  | +  [:table [:tbody
 | 
	
		
			
				|  |  | +           (let [maxrow (highest :row data)
 | 
	
		
			
				|  |  | +                 cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
 | 
	
		
			
				|  |  | +             (cons
 | 
	
		
			
				|  |  | +               (header-row cols)
 | 
	
		
			
				|  |  | +               (map #(row % cols data) (range 1 (inc maxrow)))))
 | 
	
		
			
				|  |  | +           ]])
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  (defn app []
 | 
	
		
			
				|  |  |    [:div
 | 
	
		
			
				|  |  | -   ;[:h3 "Microtables"]
 | 
	
		
			
				|  |  | -   (do
 | 
	
		
			
				|  |  | +   [:h3 "Microtables"]
 | 
	
		
			
				|  |  | +   #_(do
 | 
	
		
			
				|  |  |       (println (.stringify js/JSON (.parse mathjs "3 + 4x")))
 | 
	
		
			
				|  |  |       (println (js->clj (.parse js/JSON (.stringify js/JSON (.parse mathjs "3 + 4x")))))
 | 
	
		
			
				|  |  |       (println (type (js->clj (.stringify js/JSON (.parse mathjs "3 + 4x")))))
 | 
	
	
		
			
				|  | @@ -140,6 +166,8 @@
 | 
	
		
			
				|  |  |         (do
 | 
	
		
			
				|  |  |           (println rt)
 | 
	
		
			
				|  |  |           (println (temp2 (:evaluated rt) (:remaining rt)))))
 | 
	
		
			
				|  |  | +     (println "data-atom")
 | 
	
		
			
				|  |  | +     (println @data-atom)
 | 
	
		
			
				|  |  |       "hi")
 | 
	
		
			
				|  |  |     [sheet @data-atom]])
 | 
	
		
			
				|  |  |  
 |