From d2e20162ef6d36d8a41cb32c7d5e72cdfdcde22d Mon Sep 17 00:00:00 2001 From: Simon Brooke Date: Tue, 11 Feb 2020 13:14:36 +0000 Subject: [PATCH] Simplified syntax for Photoswipe galleries now works --- project.clj | 1 + resources/config.edn | 2 +- resources/public/content/Example gallery.md | 39 ++-- resources/public/content/Extensible Markup.md | 5 +- .../content/Simplified example gallery.md | 24 +++ src/smeagol/extensions/mermaid.clj | 2 +- src/smeagol/extensions/photoswipe.clj | 181 ++++++++++++++++++ src/smeagol/extensions/utils.clj | 75 +++++--- src/smeagol/formatting.clj | 26 +-- src/smeagol/util.clj | 3 + 10 files changed, 279 insertions(+), 79 deletions(-) create mode 100644 resources/public/content/Simplified example gallery.md create mode 100644 src/smeagol/extensions/photoswipe.clj diff --git a/project.clj b/project.clj index c92f098..b753cd1 100644 --- a/project.clj +++ b/project.clj @@ -17,6 +17,7 @@ [hiccup "1.0.5"] [im.chit/cronj "1.4.4"] [image-resizer "0.1.10"] + [instaparse "1.4.10"] [lib-noir "0.9.9" :exclusions [org.clojure/tools.reader]] [markdown-clj "0.9.99" :exclusions [com.keminglabs/cljx]] [me.raynes/fs "1.4.6"] diff --git a/resources/config.edn b/resources/config.edn index 35a85e4..059dfeb 100644 --- a/resources/config.edn +++ b/resources/config.edn @@ -36,7 +36,7 @@ "vis" smeagol.formatting/process-vega "mermaid" smeagol.extensions.mermaid/process-mermaid "backticks" smeagol.formatting/process-backticks - "pswp" smeagol.formatting/process-photoswipe} + "pswp" smeagol.extensions.photoswipe/process-photoswipe} :log-level :info ;; the minimum logging level; one of ;; :trace :debug :info :warn :error :fatal :js-from :cdnjs ;; where to load JavaScript libraries diff --git a/resources/public/content/Example gallery.md b/resources/public/content/Example gallery.md index 8379206..a2fcd00 100644 --- a/resources/public/content/Example gallery.md +++ b/resources/public/content/Example gallery.md @@ -1,8 +1,8 @@ -## The Gallery +## How this works -This page holds an example Photoswipe gallery. +The specification for this gallery is as follows: -```pswp +``` { slides: [ { src: 'content/uploads/g1.jpg', w: 2592, h:1944, @@ -21,11 +21,25 @@ This page holds an example Photoswipe gallery. ``` -## How this works +The format of the specification is [JSON](https://www.json.org/json-en.html); there are (at present) three keys, as follows -The specification for this gallery is as follows: +### slides -``` +Most be present. The value of `slides` is a list delimited by square brackets of slide objects. For more information, see the [authoritative documentation](https://photoswipe.com/documentation/getting-started.html) under the sub heading **'Creating an Array of Slide Objects'**. + +### options + +Optional. The value of `options` is a JSON object [as documented here](https://photoswipe.com/documentation/options.html). + +### openImmediately + +Optional. If the value of `openImmediately` is `true`, the gallery will open immediately, covering the whole page. If false, only a button with the label 'Open the gallery' will be shown. Selecting this button will cause the gallery to open. + +## The Gallery + +This page holds an example Photoswipe gallery. + +```pswp { slides: [ { src: 'content/uploads/g1.jpg', w: 2592, h:1944, @@ -44,16 +58,3 @@ The specification for this gallery is as follows: ``` -The format of the specification is [JSON](https://www.json.org/json-en.html); there are (at present) three keys, as follows - -### slides - -Most be present. The value of `slides` is a list delimited by square brackets of slide objects. For more information, see the [authoritative documentation](https://photoswipe.com/documentation/getting-started.html) under the sub heading **'Creating an Array of Slide Objects'**. - -### options - -Optional. The value of `options` is a JSON object [as documented here](https://photoswipe.com/documentation/options.html). - -### openImmediately - -Optional. If the value of `openImmediately` is `true`, the gallery will open immediately, covering the whole page. If false, only a button with the label 'Open the gallery' will be shown. Selecting this button will cause the gallery to open. diff --git a/resources/public/content/Extensible Markup.md b/resources/public/content/Extensible Markup.md index 3bc8301..41c9873 100644 --- a/resources/public/content/Extensible Markup.md +++ b/resources/public/content/Extensible Markup.md @@ -68,9 +68,8 @@ data/classes.mermaid ## Photoswipe galleries -Not so much a formatter, this is an extension to allow you to embed image galleries in your markdown. To specify a gallery, use three backticks followed by `pswp`, followed on the following lines by a Photoswipe specification in [JSON](https://www.json.org/json-en.html) -followed by three backticks on a line by themselves. There is an [[Example gallery]] so that you can see how this works. - +Not so much a formatter, this is an extension to allow you to embed image galleries in your markdown. To specify a gallery, use three backticks followed by `pswp`, followed on the following lines by a [Photoswipe](https://photoswipe.com/documentation/getting-started.html) specification in [JSON](https://www.json.org/json-en.html) +followed by three backticks on a line by themselves. There is an [[Example gallery]] with the full PhotoSwipe configuration, and a [[Simplified example gallery]] using a much simpler syntax, so that you can see how this works. ## Writing your own custom formatters diff --git a/resources/public/content/Simplified example gallery.md b/resources/public/content/Simplified example gallery.md new file mode 100644 index 0000000..4cff2da --- /dev/null +++ b/resources/public/content/Simplified example gallery.md @@ -0,0 +1,24 @@ +## How this works + +The specification for this gallery is as follows: + +``` +![Frost on a gate, Laurieston](content/uploads/g1.jpg) +![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg) +![Feathered snow on log, Taliesin](content/uploads/g3.jpg) +![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg) +``` + +That's all there is to it - a sequence of image links just as you'd write them anywhere else in the wiki. + +## The Gallery + +This page holds another example Photoswipe gallery, this time using a simpler, Markdown-based specification. Processing this specification takes more work than the full syntax used in the other [Example gallery], so the gallery may be slower to load; but it's much easier to configure. + +```pswp +![Frost on a gate, Laurieston](content/uploads/g1.jpg) +![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg) +![Feathered snow on log, Taliesin](content/uploads/g3.jpg) +![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg) +``` + diff --git a/src/smeagol/extensions/mermaid.clj b/src/smeagol/extensions/mermaid.clj index 9fe271b..6185b5d 100644 --- a/src/smeagol/extensions/mermaid.clj +++ b/src/smeagol/extensions/mermaid.clj @@ -1,4 +1,4 @@ -(ns ^{:doc "Format Semagol's extended markdown format." +(ns ^{:doc "Mermaid formatter for Semagol's extendsible markdown format." :author "Simon Brooke"} smeagol.extensions.mermaid (:require [smeagol.extensions.utils :refer :all] diff --git a/src/smeagol/extensions/photoswipe.clj b/src/smeagol/extensions/photoswipe.clj new file mode 100644 index 0000000..764a7a4 --- /dev/null +++ b/src/smeagol/extensions/photoswipe.clj @@ -0,0 +1,181 @@ +(ns ^{:doc "Photoswipe gallery formatter for Semagol's extendsible markdown + format." + :author "Simon Brooke"} + smeagol.extensions.photoswipe + (:require [clojure.data.json :as json] + [clojure.java.io :as cio] + [clojure.string :as cs] + [image-resizer.util :refer [buffered-image dimensions]] + [instaparse.core :as insta] + [me.raynes.fs :as fs] + [noir.io :as io] + [smeagol.configuration :refer [config]] + [smeagol.extensions.utils :refer :all] + [taoensso.timbre :as log])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;; +;;;; Smeagol: a very simple Wiki engine. +;;;; +;;;; This program is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU General Public License +;;;; as published by the Free Software Foundation; either version 2 +;;;; of the License, or (at your option) any later version. +;;;; +;;;; This program is distributed in the hope that it will be useful, +;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;;;; GNU General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU General Public License +;;;; along with this program; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +;;;; USA. +;;;; +;;;; Copyright (C) 2017 Simon Brooke +;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn process-full-photoswipe + "Process a specification for a photoswipe gallery, using a JSON + specification based on that documented on the Photoswipe website." + [^String spec ^Integer index] + (str + "
\n" + (slurp + (str (io/resource-path) "html-includes/photoswipe-boilerplate.html")) + "
+ +

+ ")) + + +(def simple-grammar + "Parser to transform a sequence of Markdown image links into something we + can build into JSON. Yes, this could all have been done with regexes, but + they are very inscrutable." + (insta/parser "SLIDE := START-CAPTION title END-CAPTION src END-SRC; + START-CAPTION := '![' ; + END-CAPTION := '](' ; + END-SRC := ')' ; + title := #'[^]]*' ; + src := #'[^)]*' ; + SPACE := #'[\\r\\n\\W]*'")) + +(defn simplify + [tree] + (if + (coll? tree) + (case (first tree) + :SLIDE (remove empty? (map simplify (rest tree))) + :title tree + :src tree + :START-CAPTION nil + :END-CAPTION nil + :END-SRC nil + (remove empty? (map simplify tree))))) + +(defn uploaded? + "Does this `url` string appear to be one that has been uploaded to our + `uploads` directory?" + [url] + (and + (cs/starts-with? (str url) "content/uploads") + (fs/exists? (cio/file upload-dir (fs/base-name url))))) + +;; (uploaded? "content/uploads/g1.jpg") + +(defn slide-merge-dimensions + "If this `slide` appears to be local, return it decorated with the + dimensions of the image it references." + [slide] + (let [url (:src slide) + dimensions (try + (if (uploaded? url) + (dimensions + (buffered-image (cio/file upload-dir (fs/base-name url))))) + (catch Exception x (.getMessage x)))] + (if dimensions + (assoc slide :w (first dimensions) :h (nth dimensions 1)) + slide))) + +;; (slide-merge-dimensions +;; {:title "Frost on a gate, Laurieston", +;; :src "content/uploads/g1.jpg"}) + +(defn process-simple-slide + [slide-spec] + (let [s (simplify (simple-grammar slide-spec)) + s'(zipmap (map first s) (map #(nth % 1) s)) + thumbsizes (:thumbnails config) + thumbsize (first + (sort + #(> (%1 thumbsizes) (%2 thumbsizes)) + (keys thumbsizes))) + url (:url s') + thumb (if + (and + (uploaded? url) + thumbsize) + (let [p (str (cio/file "uploads" (name thumbsize) (fs/base-name url))) + p' (cio/file content-dir p)] + (if + (and (fs/exists? p') (fs/readable? p')) + p)))] + (slide-merge-dimensions + (if thumb + (assoc s' :msrc thumb) + s')))) + +(def process-simple-photoswipe + "Process a simplified specification for a photoswipe gallery, comprising just + a sequence of MarkDown image links. This is REALLY expensive to do, we don't + want to do it often. Hence memoised." + (memoize + (fn + [^String spec ^Integer index] + (process-full-photoswipe + (json/write-str + {:slides (map + process-simple-slide + (re-seq #"!\[[^(]*\([^)]*\)" spec)) + ;; TODO: better to split slides in instaparse + :options { :timeToIdle 100 } + :openImmediately true}) index)))) + +;; (map +;; process-simple-slide +;; (re-seq #"!\[[^(]*\([^)]*\)" +;; "![Frost on a gate, Laurieston](content/uploads/g1.jpg) +;; ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg) +;; ![Feathered snow on log, Taliesin](content/uploads/g3.jpg) +;; ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)")) + +;; (process-simple-photoswipe +;; "![Frost on a gate, Laurieston](content/uploads/g1.jpg) +;; ![Feathered crystals on snow surface, Taliesin](content/uploads/g2.jpg) +;; ![Feathered snow on log, Taliesin](content/uploads/g3.jpg) +;; ![Crystaline growth on seed head, Taliesin](content/uploads/g4.jpg)" +;; 1) + +(defn process-photoswipe + [^String url-or-pswp-spec ^Integer index] + (let [data (resource-url-or-data->data url-or-pswp-spec) + spec (cs/trim (:data data))] + (if + (cs/starts-with? spec "![") + (process-simple-photoswipe spec index) + (process-full-photoswipe spec index)))) diff --git a/src/smeagol/extensions/utils.clj b/src/smeagol/extensions/utils.clj index 06ed74c..c73d086 100644 --- a/src/smeagol/extensions/utils.clj +++ b/src/smeagol/extensions/utils.clj @@ -2,9 +2,11 @@ :author "Simon Brooke"} smeagol.extensions.utils (:require [cemerick.url :refer (url url-encode url-decode)] + [clojure.java.io :as cjio] [clojure.string :as cs] [me.raynes.fs :as fs] [noir.io :as io] + [smeagol.configuration :refer [config]] [taoensso.timbre :as log])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -30,7 +32,17 @@ ;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(defn resource-url-or-data->data +(def content-dir + (str + (fs/absolute + (or + (:content-dir config) + (cjio/file (io/resource-path) "content"))))) + +(def upload-dir + (str (cjio/file content-dir "uploads"))) + +(def resource-url-or-data->data "Interpret this `resource-url-or-data` string as data to be digested by a `process-extension` function. It may be a URL or the pathname of a local resource, in which case the content should be fetched; or it may just be @@ -40,33 +52,34 @@ `:text`, and a key `:data` whose value is the data. There will be an additional key being the value of the `:from` key, whose value will be the source of the data." - [^String resource-url-or-data] - (let [default {:from :text - :text resource-url-or-data - :data resource-url-or-data}] - (try - (try - ;; is it a URL? - (let [url (str (url resource-url-or-data)) - result (slurp url)] - {:from :url - :url url - :data result}) - (catch java.net.MalformedURLException _ - ;; no. So is it a path to a local resource? - (let [t (cs/trim resource-url-or-data) - r (str (io/resource-path) t)] - (if - (fs/file? r) - {:from :resource - :resource t - :data (slurp r)} - default)))) - (catch Exception x - (log/error - "Could not read mermaid graph specification from `" - (cs/trim resource-url-or-data) - "` because " - (.getName (.getClass x)) - (.getMessage x) ) - default)))) + (memoize + (fn [^String resource-url-or-data] + (let [default {:from :text + :text resource-url-or-data + :data resource-url-or-data}] + (try + (try + ;; is it a URL? + (let [url (str (url resource-url-or-data)) + result (slurp url)] + {:from :url + :url url + :data result}) + (catch java.net.MalformedURLException _ + ;; no. So is it a path to a local resource? + (let [t (cs/trim resource-url-or-data) + r (str (io/resource-path) t)] + (if + (fs/file? r) + {:from :resource + :resource t + :data (slurp r)} + default)))) + (catch Exception x + (log/error + "Could not read mermaid graph specification from `" + (cs/trim resource-url-or-data) + "` because " + (.getName (.getClass x)) + (.getMessage x) ) + default)))))) diff --git a/src/smeagol/formatting.clj b/src/smeagol/formatting.clj index 357f6aa..fa4f2f6 100644 --- a/src/smeagol/formatting.clj +++ b/src/smeagol/formatting.clj @@ -6,9 +6,9 @@ [cemerick.url :refer (url url-encode url-decode)] [clj-yaml.core :as yaml] [markdown.core :as md] - [noir.io :as io] ;; used by photoswipe, only [smeagol.configuration :refer [config]] - [smeagol.extensions.mermaid :refer [process-mermaid]])) + [smeagol.extensions.mermaid :refer [process-mermaid]] + [smeagol.extensions.photoswipe :refer [process-photoswipe]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; @@ -86,28 +86,6 @@ index ");\n//]]\n")) -(defn process-photoswipe - "Process specification for a photoswipe gallery" - [^String spec ^Integer index] - (str - "
\n" - (slurp (str (io/resource-path) "html-includes/photoswipe-boilerplate.html")) - "
- -

- ")) - (defn process-backticks "Effectively, escape the backticks surrounding this `text`, by protecting them diff --git a/src/smeagol/util.clj b/src/smeagol/util.clj index 002556f..1ef44df 100644 --- a/src/smeagol/util.clj +++ b/src/smeagol/util.clj @@ -46,6 +46,9 @@ (:content-dir config) (cjio/file (io/resource-path) "content"))))) +(def upload-dir + (str (cjio/file content-dir "uploads"))) + (defn standard-params "Return a map of standard parameters to pass to the template renderer." [request]