|
@@ -42,70 +42,17 @@
|
|
|
(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 %)) (= (first (:value %)) "=")) (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))))
|
|
|
|
|
|
; CALCULATION / FORMULA EVALUATION FUNCTIONS
|
|
|
|
|
|
-(defn temp1 [data]
|
|
|
- (let [first-order (group-by #(not= (first (:value %)) "=") data)
|
|
|
- evaluated-cells (map #(keyword (str (:col %) (:row %))) (first-order true))
|
|
|
- non-evaluated-cells (map #(keyword (str (:col %) (:row %))) (first-order false))
|
|
|
- evaluated-values (map :value (first-order true))
|
|
|
- non-evaluated-values (map #(subs (:value %) 1) (first-order false))]
|
|
|
- {: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)))))
|
|
|
(def evaluate-expression (memoize (fn [expression variables]
|
|
|
(.evaluate mathjs expression (clj->js variables)))))
|
|
|
|
|
|
-(defn temp3 [[k v]]
|
|
|
- (let [w (name k)
|
|
|
- c (re-find #"^[A-Z]+" w)
|
|
|
- r (.parseInt js/window (re-find #"[0-9]+$" w))]
|
|
|
- (do
|
|
|
- (println "looking at cell " c r)
|
|
|
- (update-display c r v)
|
|
|
- )
|
|
|
- )
|
|
|
- )
|
|
|
-
|
|
|
-(defn temp2 [evaluated remaining]
|
|
|
- (if (empty? remaining)
|
|
|
- evaluated
|
|
|
- (let [vars (map #(parse-variables (val %)) remaining)
|
|
|
- remaining-vars (zipmap (keys remaining) vars)
|
|
|
- evaluated-vars (map (fn [x] (map #(let [item (keyword %)]
|
|
|
- (cond
|
|
|
- (contains? evaluated item) [% (evaluated item)]
|
|
|
- (contains? remaining item) [% :not-yet]
|
|
|
- :else [% 0]
|
|
|
- )) x)) vars)
|
|
|
- parsed (zipmap (keys remaining) evaluated-vars)
|
|
|
- not-ready (group-by (fn [[ k v ]] (some #(= :not-yet (last %)) v)) parsed)
|
|
|
- ;TODO: detect circular references
|
|
|
- prepared (map (fn [[k v]] [k (evaluate-expression (remaining k) (apply hash-map (flatten v)))]) (not-ready nil))
|
|
|
- new-evaluated (reduce conj evaluated prepared)
|
|
|
- still-remaining (apply hash-map (flatten (map #(list (key %) (remaining (key %))) (not-ready true))))]
|
|
|
- ;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])}
|
|
|
- ;not-ready ; {true [[:B8 ([B7 :not-yet])]], nil [[:C7 ([D1 0])] [:B7 ([C5 269] [D6 4065])]]}
|
|
|
- ;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
|
|
|
- )
|
|
|
- ))
|
|
|
-
|
|
|
(def str->rc (memoize (fn [s]
|
|
|
(let [c (re-find #"^[A-Z]+" s)
|
|
|
r (.parseInt js/window (re-find #"[0-9]+$" s))]
|
|
@@ -121,19 +68,12 @@
|
|
|
(nil? v) 0
|
|
|
(and (string? v) (= (first v) "=")) :not-yet
|
|
|
:else v)))
|
|
|
-(defn copy-display-values' [data display-values]
|
|
|
- (if (empty? display-values)
|
|
|
- data
|
|
|
- (let [v (first display-values)
|
|
|
- r (:row v)
|
|
|
- c (:col v)
|
|
|
- inserted (map #(if (and (= r (:row %)) (= c (:col %)) (= (first (:value %)) "=")) (assoc % :display (:display v)) %) data)]
|
|
|
- (recur inserted (rest display-values)))))
|
|
|
(defn copy-display-values [data display-values]
|
|
|
(let [removed (map #(-> % (dissoc :vars) (dissoc :refs) (dissoc :found) (dissoc :inputs)) display-values)]
|
|
|
(into data removed)))
|
|
|
|
|
|
-(defn experimental [data]
|
|
|
+;TODO: figure out how to re-evaluate only when the cell modified affects other cells
|
|
|
+(defn re-evaluate [data]
|
|
|
(let [remove-old-displays (map #(dissoc % :display) data)
|
|
|
{has-formula true original-values false} (group-by #(= (first (:value %)) "=") remove-old-displays)
|
|
|
;TODO: detect circular references
|
|
@@ -144,48 +84,10 @@
|
|
|
{not-ready true ready nil} (group-by (fn [datum] (some #(= :not-yet %) (:found datum))) search-values)
|
|
|
prepped-for-eval (map (fn [datum] (assoc datum :inputs (apply hash-map (interleave (:vars datum) (:found datum))))) ready)
|
|
|
evaluated (map (fn [datum] (assoc datum :display (evaluate-expression (subs (:value datum) 1) (:inputs datum)))) prepped-for-eval)
|
|
|
- updated-values (copy-display-values values evaluated)
|
|
|
- ]
|
|
|
- (println)
|
|
|
- (println)
|
|
|
- (println)
|
|
|
- (println "has-formula" has-formula)
|
|
|
- (println "values" values)
|
|
|
- (println "variables" variables)
|
|
|
- (println "mapped-cell-keys" mapped-cell-keys)
|
|
|
- (println "search-values" search-values)
|
|
|
- (println "not ready to eval" not-ready)
|
|
|
- (println " nil?" (nil? not-ready))
|
|
|
- (println "ready to eval" ready)
|
|
|
- (println "prepped-for-eval" prepped-for-eval)
|
|
|
- (println "evaluated" evaluated)
|
|
|
- (println "updated-values" updated-values)
|
|
|
- (println "done!")
|
|
|
- (println)
|
|
|
- (println "loop " values " " mapped-cell-keys)
|
|
|
- (println "")
|
|
|
- (println "recur " updated-values " " not-ready)
|
|
|
+ updated-values (copy-display-values values evaluated)]
|
|
|
(if (nil? not-ready)
|
|
|
updated-values
|
|
|
- (recur updated-values not-ready)
|
|
|
- )
|
|
|
- ))
|
|
|
- )
|
|
|
- )
|
|
|
-
|
|
|
-;TODO: figure out how to re-evaluate only when the cell modified affects other cells
|
|
|
-(defn re-evaluate-data []
|
|
|
- (let [rt (temp1 @data-atom)
|
|
|
- new-data (temp2 (:evaluated rt) (:remaining rt))
|
|
|
- update-results (map temp3 new-data)
|
|
|
- ]
|
|
|
- (do
|
|
|
- (println rt)
|
|
|
- (println new-data)
|
|
|
- (println "updating sheet data with above new data")
|
|
|
- (println update-results)
|
|
|
- (println "done")
|
|
|
- )))
|
|
|
+ (recur updated-values not-ready))))))
|
|
|
|
|
|
|
|
|
(defn on-enter-cell [c r e]
|
|
@@ -194,7 +96,7 @@
|
|
|
(defn on-leave-cell [c r e]
|
|
|
(println (str "leaving cell " c r))
|
|
|
(toggle-display c r :display)
|
|
|
- (re-evaluate-data))
|
|
|
+ (swap! data-atom re-evaluate))
|
|
|
|
|
|
|
|
|
;; -------------------------
|
|
@@ -205,9 +107,7 @@
|
|
|
^{: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 (partial on-leave-cell c r)
|
|
|
- :on-focus (partial on-enter-cell c r)}]]
|
|
|
- )
|
|
|
- )
|
|
|
+ :on-focus (partial on-enter-cell c r)}]]))
|
|
|
(defn row [r cols data]
|
|
|
^{:key (str "row-" r)} [:tr
|
|
|
(cons
|
|
@@ -225,23 +125,21 @@
|
|
|
cols (take-while (partial not= (next-letter (highest :col data))) col-letters)]
|
|
|
(cons
|
|
|
(header-row cols)
|
|
|
- (map #(row % cols data) (range 1 (inc maxrow)))))
|
|
|
- ]])
|
|
|
+ (map #(row % cols data) (range 1 (inc maxrow)))))]])
|
|
|
|
|
|
(defn app []
|
|
|
[:div
|
|
|
[:h3 "Microtables"]
|
|
|
- (do
|
|
|
- (println "hi again")
|
|
|
- (experimental @data-atom)
|
|
|
- "hi again")
|
|
|
[sheet @data-atom]])
|
|
|
|
|
|
;; -------------------------
|
|
|
;; Initialize app
|
|
|
|
|
|
+(swap! data-atom re-evaluate) ; evalutate any formulas the first time
|
|
|
+
|
|
|
(defn mount-root []
|
|
|
(r/render [app] (.getElementById js/document "app")))
|
|
|
|
|
|
(defn init! []
|
|
|
(mount-root))
|
|
|
+
|