Skip to content

Commit

Permalink
Allow the class on the generated toc to be customized
Browse files Browse the repository at this point in the history
Addresses #129
  • Loading branch information
lacarmen committed Dec 10, 2019
1 parent 5db0e63 commit 8c07f46
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 55 deletions.
59 changes: 35 additions & 24 deletions src/cryogen_core/compiler.clj
Original file line number Diff line number Diff line change
Expand Up @@ -100,45 +100,56 @@
:page-meta page-meta
:content content})))

(defn add-toc
"Adds :toc to article, if necessary"
[{:keys [content toc toc-class] :as article} config]
(update
article
:toc
#(if %
(toc/generate-toc content
{:list-type toc
:toc-class (or toc-class (:toc-class config) "toc")}))))

(defn merge-meta-and-content
"Merges the page metadata and content maps, adding :toc if necessary."
"Merges the page metadata and content maps"
[file-name page-meta content]
(merge
(update-in page-meta [:layout] #(str (name %) ".html"))
{:file-name file-name
:content content
:toc (if-let [toc (:toc page-meta)]
(toc/generate-toc content :list-type toc))}))
:content content}))

(defn parse-page
"Parses a page/post and returns a map of the content, uri, date etc."
[page config markup]
(let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge
(merge-meta-and-content file-name (update page-meta :layout #(or % :page)) content)
{:uri (page-uri file-name :page-root-uri config)
:page-index (:page-index page-meta)
:klipse/global (:klipse config)
:klipse/local (:klipse page-meta)})))
(-> (merge-meta-and-content file-name (update page-meta :layout #(or % :page)) content)
(merge
{:uri (page-uri file-name :page-root-uri config)
:page-index (:page-index page-meta)
:klipse/global (:klipse config)
:klipse/local (:klipse page-meta)})
(add-toc config))))

(defn parse-post
"Return a map with the given post's information."
[page config markup]
(let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge
(merge-meta-and-content file-name (update page-meta :layout #(or % :post)) content)
(let [date (if (:date page-meta)
(.parse (java.text.SimpleDateFormat. (:post-date-format config)) (:date page-meta))
(parse-post-date file-name (:post-date-format config)))
archive-fmt (java.text.SimpleDateFormat. (:archive-group-format config) (Locale/getDefault))
formatted-group (.format archive-fmt date)]
{:date date
:formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group)
:uri (page-uri file-name :post-root-uri config)
:tags (set (:tags page-meta))
:klipse/global (:klipse config)
:klipse/local (:klipse page-meta)}))))
(let [date (if (:date page-meta)
(.parse (java.text.SimpleDateFormat. (:post-date-format config)) (:date page-meta))
(parse-post-date file-name (:post-date-format config)))
archive-fmt (java.text.SimpleDateFormat. (:archive-group-format config) (Locale/getDefault))
formatted-group (.format archive-fmt date)]
(-> (merge-meta-and-content file-name (update page-meta :layout #(or % :post)) content)
(merge
{:date date
:formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group)
:uri (page-uri file-name :post-root-uri config)
:tags (set (:tags page-meta))
:klipse/global (:klipse config)
:klipse/local (:klipse page-meta)})
(add-toc config)))))

(defn read-posts
"Returns a sequence of maps representing the data from markdown files of posts.
Expand Down
26 changes: 14 additions & 12 deletions src/cryogen_core/toc.clj
Original file line number Diff line number Diff line change
Expand Up @@ -69,37 +69,39 @@
(defn- build-toc
"Given the root of a toc tree and either :ol or :ul,
generate the table of contents and return it as a hiccup tree."
[toc-tree list-open & {:keys [outer-list?] :or {outer-list? true}}]
[toc-tree list-type toc-class & [{:keys [outer-list?] :or {outer-list? true}}]]
(let [{:keys [children], {:keys [anchor text]} :value} toc-tree
li (toc-entry anchor text)]
(if (seq children)
;; Create hiccup sequence of :ol/:ul tag and sequence of :li tags
(list li [list-open
(list li [list-type
(when outer-list?
{:class "content"})
(map #(build-toc % list-open :outer-list? false) children)])
{:class toc-class})
(map #(build-toc % list-type toc-class {:outer-list? false}) children)])
li))) ; Or just return the naked :li tag

(defn generate-toc*
"The inner part of generate-toc. Takes maps of enlive-style html elements
and returns hiccup."
[elements list-type]
[elements list-type toc-class]
(-> elements
(get-headings)
(build-toc-tree)
(build-toc list-type)))
(build-toc list-type toc-class)))

(defn generate-toc
"Reads an HTML string and parses it for headers, then returns a list of links
to them.
Optionally, a map of :list-type can be provided with value :ul, :ol, or true.
:ol and true will result in an ordered list being generated for the table of
contents, while :ul will result in an unordered list. The default is an
ordered list."
[html & {:keys [list-type] :or {list-type :ol}}]
A map of :list-type and :toc-class should be provided.
:list-type can be one of :ul, :ol, or true.
:ol and true will result in an ordered list being generated for the table of
contents, while :ul will result in an unordered list. The default is an
ordered list.
:toc-class will be added to the top-level element (ie. ul.toc-class or ol.toc-class)"
[html {:keys [list-type toc-class]}]
(let [list-type (if (true? list-type) :ol list-type)]
(-> html
(enlive/html-snippet)
(generate-toc* list-type)
(generate-toc* list-type toc-class)
(hiccup/html))))
36 changes: 17 additions & 19 deletions test/cryogen_core/toc_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@
; * h3
; * h1
(deftest test-generate-toc*
(is (util/hic= [:ol.content [:li [:a {:href "#test"} "Test"]]]
(is (util/hic= [:ol.toc [:li [:a {:href "#test"} "Test"]]]
(-> [:div [:h2 {:id "test"} "Test"]]
(enlive/html)
(generate-toc* :ol))))
(generate-toc* :ol "toc"))))

(is (-> [:div [:p "This is not a header"]]
(enlive/html)
(generate-toc* :ol)
(generate-toc* :ol "toc")
(nil?)))

(is (util/hic= [:ol.content
(is (util/hic= [:ol.toc
[:li [:a {:href "#starting_low"} "Starting Low"]]
[:li [:a {:href "#finishing_high"} "Finishing High"]]]
(-> [:div
Expand All @@ -51,10 +51,10 @@
[:h1 {:id "finishing_high"}
"Finishing High"]]
(enlive/html)
(generate-toc* :ol)))
(generate-toc* :ol "toc")))
"No outer header should be less indented than the first header tag.")

(is (util/hic= [:ul.content
(is (util/hic= [:ul.toc
[:li [:a {:href "#starting_low"} "Starting Low"]]
[:ul
[:li [:a {:href "#jumping_in"} "Jumping Right In"]]
Expand All @@ -70,34 +70,32 @@
[:h2 {:id "to_the_top"}
"To the top"]]
(enlive/html)
(generate-toc* :ul)))
(generate-toc* :ul "toc")))
(str "Inner headers can be more indented, "
"but outer headers cannot be less indented "
"than the original header."))

(is (util/hic= [:ol.content
(is (util/hic= [:ol.toc
[:li [:a {:href "#foo_<code>bar</code>"} "foo " [:code "bar"]]]]
(-> [:div [:h2 {:id "foo_<code>bar</code>"} "foo " [:code "bar"]]]
(enlive/html)
(generate-toc* :ol)))
(generate-toc* :ol "toc")))
"Supports code tags in headings.")

(is (util/hic= [:ol.content
(is (util/hic= [:ol.toc
[:li [:a {:href "#foo_<strong>bar_<i>baz</i></strong>"}
"foo " [:strong "bar " [:i "baz"]]]]]
(-> [:div [:h2 {:id "foo_<strong>bar_<i>baz</i></strong>"}
"foo " [:strong "bar " [:i "baz"]]]]
(enlive/html)
(generate-toc* :ol)))
(generate-toc* :ol "toc")))
"Supports nested tags in headings."))

(deftest test-generate-toc
(let [htmlString "<div><h2 id=\"test\">Test</h2></div>"]
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString)))
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString :list-type true)))
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString :list-type :ol)))
(is (= "<ul class=\"content\"><li><a href=\"#test\">Test</a></li></ul>"
(generate-toc htmlString :list-type :ul)))))
(is (= "<ol class=\"toc\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString {:list-type true :toc-class "toc"})))
(is (= "<ol class=\"toc\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString {:list-type :ol :toc-class "toc"})))
(is (= "<ul class=\"custom-toc\"><li><a href=\"#test\">Test</a></li></ul>"
(generate-toc htmlString {:list-type :ul :toc-class "custom-toc"})))))

0 comments on commit 8c07f46

Please sign in to comment.