reagent/demo/sitetools/core.cljs

221 lines
6.8 KiB
Plaintext
Raw Normal View History

(ns sitetools.core
(:require [clojure.string :as string]
[goog.events :as evt]
[goog.history.EventType :as hevt]
[reagent.core :as r]
2015-09-07 20:22:26 +00:00
[secretary.core :as secretary :refer-macros [defroute]]
[reagent.debug :refer-macros [dbg log dev?]]
[reagent.interop :as i :refer-macros [.' .!]])
2015-09-07 22:41:04 +00:00
(:import goog.History
goog.history.Html5History))
(when (exists? js/console)
(enable-console-print!))
2015-09-07 20:22:26 +00:00
(defn rswap! [a f & args]
2015-09-07 22:12:41 +00:00
;; Roughly like swap!, except that recursive swaps are ok
2015-09-08 06:38:45 +00:00
(let [fs (or (.-rswapfs a)
(set! (.-rswapfs a) (array)))]
2015-09-07 20:22:26 +00:00
(.push fs #(apply f % args))
2015-09-08 11:08:09 +00:00
(if (< 1 (alength fs))
2015-09-07 22:12:41 +00:00
nil
(let [f' (fn [state]
2015-09-08 11:08:09 +00:00
;;;; TODO: This could throw
2015-09-07 22:12:41 +00:00
(let [s ((aget fs 0) state)]
(.shift fs)
2015-09-08 11:08:09 +00:00
(if (-> fs alength pos?)
2015-09-07 22:12:41 +00:00
(recur s)
s)))]
(swap! a f')))))
2015-09-07 20:22:26 +00:00
;;; Configuration
2015-09-07 22:29:06 +00:00
(declare page-content)
2015-09-08 06:52:10 +00:00
(defonce config (r/atom {;;:page-map {"index.html" [:div "Empty"]}
;;:page-titles {}
:body [page-content]
2015-09-07 20:22:26 +00:00
:main-content [:div]
:pages #{}
:site-dir "outsite/public"
:css-infiles ["site/public/css/main.css"]
:css-file "css/built.css"
:js-file "js/main.js"
:js-dir "js/out"
:main-div "main-content"
2015-09-08 11:08:09 +00:00
:default-title ""}))
2015-09-07 20:22:26 +00:00
(defonce history nil)
(defn demo-handler [state [id v1 v2 :as event]]
(case id
:content (do
2015-09-07 22:29:06 +00:00
(let [title (if v2
2015-09-08 11:08:09 +00:00
(str (:title-prefix state) v2)
(str (:default-title state)))]
2015-09-07 22:12:41 +00:00
(when r/is-client
(r/next-tick #(set! js/document.title title)))
(assoc state
:main-content v1
:title title)))
:set-page (do (secretary/dispatch! v1)
2015-09-08 07:35:55 +00:00
(assoc state :page-name v1))
2015-09-07 20:22:26 +00:00
:goto-page (do
2015-09-07 22:12:41 +00:00
(when r/is-client
(.setToken history v1 false)
(r/next-tick #(set! js/document.body.scrollTop 0)))
2015-09-08 11:08:09 +00:00
(recur state [:set-page v1]))
2015-09-07 20:22:26 +00:00
state))
(defn dispatch [event]
2015-09-08 11:08:09 +00:00
;; (dbg event)
2015-09-07 20:22:26 +00:00
(rswap! config demo-handler event)
nil)
(defn reg-page [url]
(swap! config update-in [:pages] conj url))
2015-09-08 11:08:09 +00:00
;;; History
2015-09-07 20:22:26 +00:00
(defn init-history []
(when-not history
2015-09-08 07:35:55 +00:00
(let [page (:page-name @config)
2015-09-08 11:08:09 +00:00
html5 (and page
(.isSupported Html5History)
(#{"http:" "https:"} js/location.protocol))]
2015-09-08 07:35:55 +00:00
(doto (set! history
(if html5
(doto (Html5History.)
(.setUseFragment false)
2015-09-08 11:08:09 +00:00
(.setPathPrefix (-> js/location.pathname
(string/replace
(re-pattern (str page "$")) "")
(string/replace #"/*$" ""))))
2015-09-08 07:35:55 +00:00
(History.)))
2015-09-08 11:08:09 +00:00
(evt/listen hevt/NAVIGATE #(dispatch [:set-page (.-token %)]))
2015-09-08 07:35:55 +00:00
(.setEnabled true))
2015-09-08 11:08:09 +00:00
(when (and page (not html5) (empty? (.getToken history)))
(dispatch [:set-page page])))))
2015-09-07 20:22:26 +00:00
2015-09-08 11:08:09 +00:00
(defn as-relative [f]
(string/replace f #"^/" ""))
;;; Components
2015-09-07 20:22:26 +00:00
(defn link [props child]
[:a (assoc props
2015-09-08 11:08:09 +00:00
:href (-> props :href as-relative)
2015-09-07 20:22:26 +00:00
:on-click #(do (.preventDefault %)
(dispatch [:goto-page (:href props)])))
child])
(defn page-content []
2015-09-07 21:01:33 +00:00
(:main-content @config))
2015-09-08 11:08:09 +00:00
;;; Static site generation
2015-09-08 06:52:10 +00:00
(defn prefix [href page]
2015-09-08 11:08:09 +00:00
(let [depth (-> #"/" (re-seq (as-relative page)) count)]
(str (->> "../" (repeat depth) (apply str)) href)))
(defn body []
(let [b (:body @config)]
(assert (vector? b) (str "body is not a vector: " b))
b))
(defn danger [t s]
[t {:dangerouslySetInnerHTML {:__html s}}])
(defn html-template [{:keys [title body timestamp page-conf
opt-none req]}]
(let [c @config
main (str (:js-file c) timestamp)
css-file (:css-file c)
opt-none (:opt-none c)]
(r/render-to-static-markup
[:html
[:head
[:meta {:charset "utf-8"}]
[:meta {:name 'viewport
:content "width=device-width, initial-scale=1.0"}]
2015-09-08 06:52:10 +00:00
[:base {:href (prefix "" (:page-name page-conf))}]
[:link {:href (str css-file timestamp) :rel 'stylesheet}]
[:title title]]
[:body
2014-12-09 06:30:57 +00:00
[:div {:id (:main-div @config)}
(danger :div body)]
(danger :script (str "var pageConfig = " (-> page-conf
clj->js
js/JSON.stringify)))
[:script {:src main :type "text/javascript"}]]])))
(defn gen-page [page-name timestamp]
2015-09-08 06:52:10 +00:00
;; (reset! page page-name)
(dispatch [:set-page page-name])
(let [b (r/render-component-to-string (body))]
(str "<!doctype html>"
2015-09-08 11:08:09 +00:00
(html-template {:title (:title @config)
:body b
2015-09-08 11:08:09 +00:00
:page-conf {:page-name page-name}
:timestamp timestamp}))))
(defn mkdirs [f]
(let [fs (js/require "fs")
path (js/require "path")
items (as-> f _
2015-09-07 20:22:26 +00:00
(.' path dirname _)
(.' path normalize _)
2015-09-08 11:08:09 +00:00
(string/split _ #"/"))]
(doseq [d (reductions #(str %1 "/" %2) items)]
(when-not (.' fs existsSync d)
(.' fs mkdirSync d)))))
(defn write-file [f content]
(let [fs (js/require "fs")]
(mkdirs f)
(.' fs writeFileSync f content)))
(defn read-file [f]
(let [fs (js/require "fs")]
(.' fs readFileSync f)))
(defn path-join [& paths]
(let [path (js/require "path")]
(apply (.' path :join) paths)))
(defn read-css []
2015-09-08 11:08:09 +00:00
(string/join "\n" (map read-file (:css-infiles @config))))
(defn write-resources [dir]
(write-file (path-join dir (:css-file @config))
(read-css)))
;;; Main entry points
(defn ^:export genpages [opts]
(log "Generating site")
(swap! config merge (js->clj opts :keywordize-keys true))
(let [dir (:site-dir @config)
2015-09-08 11:08:09 +00:00
timestamp (str "?" (js/Date.now))]
2015-09-08 06:52:10 +00:00
(doseq [f (:pages @config)]
(write-file (path-join dir (dbg (as-relative f)))
(gen-page f timestamp)))
(write-resources dir))
(log "Wrote site"))
(defn start! [site-config]
(swap! config merge site-config)
(when r/is-client
(let [conf (when (exists? js/pageConfig)
2015-09-08 11:08:09 +00:00
(js->clj js/pageConfig :keywordize-keys true))]
(swap! config merge conf)
2015-09-08 07:35:55 +00:00
(init-history)
(r/render-component (body)
2015-09-08 11:08:09 +00:00
(js/document.getElementById (:main-div @config))))))