From afdc0f20b3078b0101113b7304730cce774ea83e Mon Sep 17 00:00:00 2001 From: teesloane Date: Wed, 27 May 2020 07:37:10 -0400 Subject: [PATCH] Feat: add clean anchor and internal link handler. Add: empty footnote ref/def classes. Add: add sluggified id's to headlines. Fix: add code child to get-headline-helper. Clean: remove comments. Test: get-headline-helper. --- .../firn/_firn_starter/static/css/main.css | 4 ++ clojure/src/firn/markup.clj | 53 +++++++++++++------ clojure/src/firn/org.clj | 23 ++++---- clojure/test/firn/markup_test.clj | 7 +++ clojure/test/firn/org_test.clj | 22 ++++++++ 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/clojure/resources/firn/_firn_starter/static/css/main.css b/clojure/resources/firn/_firn_starter/static/css/main.css index d9e7a612..fd167a1c 100644 --- a/clojure/resources/firn/_firn_starter/static/css/main.css +++ b/clojure/resources/firn/_firn_starter/static/css/main.css @@ -143,3 +143,7 @@ img { .firn-headline-timestamp {} .firn-headline-cookie {} + +/* Other "Components" */ +.firn-footnote-ref {} +.firn-footnote-def {} diff --git a/clojure/src/firn/markup.clj b/clojure/src/firn/markup.clj index 9084b34b..98a6bc97 100644 --- a/clojure/src/firn/markup.clj +++ b/clojure/src/firn/markup.clj @@ -1,7 +1,8 @@ (ns firn.markup "Namespace responsible for converted org-edn into html." (:require [clojure.string :as s] - [firn.util :as u])) + [firn.util :as u] + [firn.org :as org])) (declare to-html) @@ -32,6 +33,25 @@ [:span.firn-img-caption desc]] [:img {:src path}])) +(defn- clean-anchor + "converts `::*My Heading` => #my-heading" + [anchor] + (str "#" (-> anchor + (s/replace #"::\*" "") + (s/replace #" " "-") + (s/lower-case)))) + +(defn internal-link-handler + "Takes an org link and converts it into an html path." + [org-link] + (let [regex #"(file:)(.*)\.(org)(\:\:\*.+)?" + res (re-matches regex org-link) + anchor-link (last res) + anchor-link (when anchor-link (-> res last clean-anchor))] + (if anchor-link + (str "./" (nth res 2) anchor-link) + (str "./" (nth res 2))))) + (defn link->html "Parses links from the org-tree. Checks if a link is an HTTP link or File link." @@ -43,12 +63,11 @@ img-http-regex #"(http:\/\/|https:\/\/)(.*)\.(jpg|JPG|gif|GIF|png)" img-rel-regex #"(\.(.*))\.(jpg|JPG|gif|GIF|png)" img-make-url #(->> (re-matches img-file-regex link-href) - (take-last 2) - (s/join ".")) + (take-last 2) + (s/join ".")) ;; file regexs / ctor fns - org-file-regex #"(file:)(.*)\.(org)" - http-link-regex #"https?:\/\/(?![^\" ]*(?:jpg|png|gif))[^\" ]+" - file-path #(str "./" (nth % 2))] + org-file-regex #"(file:)(.*)\.(org)(\:\:\*.+)?" + http-link-regex #"https?:\/\/(?![^\" ]*(?:jpg|png|gif))[^\" ]+"] (cond ;; Images --- @@ -66,7 +85,7 @@ ;; org files (re-matches org-file-regex link-href) - [:a.firn_internal {:href (file-path (re-matches org-file-regex link-href))} link-val] + [:a.firn_internal {:href (internal-link-handler link-href)} link-val] (re-matches http-link-regex link-href) [:a.firn_external {:href link-href :target "_blank"} link-val] @@ -85,15 +104,19 @@ keywrd (v :keyword) priority (v :priority) value (v :value) + parent {:type "headline" :level level :children [v]} heading-priority (u/str->keywrd "span.firn-headline-priority.firn-headline-priority__" priority) heading-keyword (u/str->keywrd "span.firn-headline-keyword.firn-headline-keyword__" keywrd) + heading-anchor (-> parent org/get-headline-helper clean-anchor) + ;; _ (prn "heading anchor is " heading-anchor) + heading-id+class #(u/str->keywrd "h" % heading-anchor ".firn-headline.firn-headline-" %) h-level (case level - 1 :h1.firn-headline.firn-headline-1 - 2 :h2.firn-headline.firn-headline-2 - 3 :h3.firn-headline.firn-headline-3 - 4 :h4.firn-headline.firn-headline-4 - 5 :h5.firn-headline.firn-headline-5 - :h6.firn-headline-6) + 1 (heading-id+class 1) + 2 (heading-id+class 2) + 3 (heading-id+class 3) + 4 (heading-id+class 4) + 5 (heading-id+class 5) + (heading-id+class 6)) make-child #(into [%] (map title->html children))] (case typ "headline" (make-child :div) @@ -114,7 +137,7 @@ (defn- footnote-ref [v] - [:a.firn_footnote-ref + [:a.firn-footnote-ref {:id (str "fn-" (v :label)) :href (str "#" (v :label))} [:sup (v :label)]]) @@ -122,7 +145,7 @@ (defn- footnote-def [v] (let [make-child #(into [%] (map to-html (v :children)))] - [:span.firn_footnote-def + [:span.firn-footnote-def [:span {:id (v :label) :style "padding-right: 8px"} (v :label)] (make-child :span) diff --git a/clojure/src/firn/org.clj b/clojure/src/firn/org.clj index 27e3c754..0dd398ce 100644 --- a/clojure/src/firn/org.clj +++ b/clojure/src/firn/org.clj @@ -22,7 +22,7 @@ (prn "Orgize failed to parse file." stripped res) (res :out))))) -(defn- get-headline-helper +(defn get-headline-helper "Sanitizes a heading of links and just returns text. Necessary because org leafs of :type `link` have a `:desc` and not a `:value` @@ -48,15 +48,16 @@ (case (:type child) "text" (get-trimmed-val child :value) "link" (get-trimmed-val child :desc) + "code" (get-trimmed-val child :value) ""))))))) (defn get-headline "Fetches a headline from an org-mode tree." [tree name] (->> (tree-seq map? :children tree) - (filter #(and (= "headline" (:type %)) - (= name (get-headline-helper %)))) - (first))) + (filter #(and (= "headline" (:type %)) + (= name (get-headline-helper %)))) + (first))) (defn get-headline-content "Same as get-headline, but removes the first child :title)." @@ -100,7 +101,6 @@ :log-sum log-sum :hour-sum (u/timestr->hour-float log-sum)})))) - (defn logbook-year-stats "Takes a logbook and pushes it's data into a year calendar. Returns a map that looks like: @@ -133,21 +133,18 @@ :as opts}] [:div (for [[year year-of-logs] (logbook-year-stats logbook) - :let [ - max-log (apply max-key :hour-sum year-of-logs) ;; Don't need this yet. + :let [max-log (apply max-key :hour-sum year-of-logs) ;; Don't need this yet. ;; This should be measured against the height and whatever the max-log is. g-multiplier (/ height 8) ;; 8 - max hours we expect someone to log in a day fmt-points #(str %1 "," (* g-multiplier (%2 :hour-sum))) points (s/join " " (->> year-of-logs (map-indexed fmt-points)))]] - [:div [:h5.firn-headline.firn-headline-5 year] - [:svg {:viewbox (format "0 0 %s %s" width height), + [:svg {:viewbox (format "0 0 %s %s" width height) :class "chart"} [:g {:transform (format "translate(0, %s) scale(1, -1)", (- height (* stroke-width 1.25)))} - [:polyline {:fill "none", - :stroke stroke, - :stroke-width "1", + [:polyline {:fill "none" + :stroke stroke + :stroke-width "1" :points points}]]]])])) - diff --git a/clojure/test/firn/markup_test.clj b/clojure/test/firn/markup_test.clj index 892d1712..5232d7a3 100644 --- a/clojure/test/firn/markup_test.clj +++ b/clojure/test/firn/markup_test.clj @@ -43,3 +43,10 @@ (t/testing "internal-link" (t/is (= (sut/link->html (sample-links :file-link)) [:a.firn_internal {:href "./file2"} "File 2"])))) + +(t/deftest internal-link-handler + (t/testing "Expected results." + (let [res1 (sut/internal-link-handler "file:foo.org") + res2 (sut/internal-link-handler "file:foo.org::*my headline link")] + (t/is (= res1 "./foo")) + (t/is (= res2 "./foo#my-headline-link"))))) diff --git a/clojure/test/firn/org_test.clj b/clojure/test/firn/org_test.clj index 99da7c11..89177a04 100644 --- a/clojure/test/firn/org_test.clj +++ b/clojure/test/firn/org_test.clj @@ -23,6 +23,28 @@ (t/testing "It returns the expected value." (t/is (= (-> res :children first :type) "section"))))) +(t/deftest get-headline-helper + (t/testing "expected output" + (let [sample-data + {:type "headline", + :level 1, + :children + [{:type "title", :level 1, :tags ["ATTACH"], :raw "Image Tests", :children [{:type "text", :value "Image Tests"}]}]} + + sample-data-with-multiple-children + {:type "headline", + :level 1, + :children + [{:type "title", :level 1, :raw "Headlines <2020-03-27 Fri>", :properties {:foo "bar"}, + :children [{:type "text", :value "Headlines "} {:type "timestamp", :timestamp_type "active", :start {:year 2020, :month 3, :day 27, :dayname "Fri"}}]}]} + + res1 (sut/get-headline-helper sample-data) + res2 (sut/get-headline-helper sample-data-with-multiple-children)] + + (t/is (= res1 "Image Tests")) + (t/is (= res2 "Headlines"))))) + + (t/deftest parsed-org-date->unix-time (t/testing "returns the expected value." (t/is (= 1585683360000