Skip to content

Commit

Permalink
Simplified syntax for Photoswipe galleries now works
Browse files Browse the repository at this point in the history
  • Loading branch information
simon-brooke committed Feb 11, 2020
1 parent fc4dcdb commit d2e2016
Show file tree
Hide file tree
Showing 10 changed files with 279 additions and 79 deletions.
1 change: 1 addition & 0 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
2 changes: 1 addition & 1 deletion resources/config.edn
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
39 changes: 20 additions & 19 deletions resources/public/content/Example gallery.md
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -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.
5 changes: 2 additions & 3 deletions resources/public/content/Extensible Markup.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
24 changes: 24 additions & 0 deletions resources/public/content/Simplified example gallery.md
Original file line number Diff line number Diff line change
@@ -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)
```

2 changes: 1 addition & 1 deletion src/smeagol/extensions/mermaid.clj
Original file line number Diff line number Diff line change
@@ -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]
Expand Down
181 changes: 181 additions & 0 deletions src/smeagol/extensions/photoswipe.clj
Original file line number Diff line number Diff line change
@@ -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
"<div class=\"pswp\" id=\"pswp-"
index "\" tabindex=\"-1\" role=\"dialog\" aria-hidden=\"true\">\n"
(slurp
(str (io/resource-path) "html-includes/photoswipe-boilerplate.html"))
"</div>
<script>
\n//<![CDATA[\n
var pswpElement = document.getElementById('pswp-" index "');
var spec" index " = "
spec
";
var gallery" index
" = new PhotoSwipe( pswpElement, PhotoSwipeUI_Default, spec"
index ".slides, spec" index ".options);
if (spec" index ".openImmediately) { gallery" index ".init(); }
\n//]]\n
</script>
<p><button onclick=\"gallery" index
".init()\">Open the gallery</button></p>
</div>"))


(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))))
75 changes: 44 additions & 31 deletions src/smeagol/extensions/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Expand All @@ -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
Expand All @@ -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))))))
Loading

0 comments on commit d2e2016

Please sign in to comment.