foto: image album browser and control
authorMischa POSLAWSKY <perl@shiar.org>
Mon, 18 Sep 2017 16:54:42 +0000 (18:54 +0200)
committerMischa POSLAWSKY <perl@shiar.org>
Tue, 19 Jun 2018 11:10:22 +0000 (13:10 +0200)
Original foto.php controller and related assets copied from Lijtweg at
commit v2.5-24-ga5399c5d03 (2017-10-22).

Requires image files at data/ symlinked into albums at foto/ with prefixed
resolution attributes.  Depends on external javascript libraries [PhotoSwipe
and Flickr's Justified-Layout] at lib/ (changed from import/ at lijtweg)
installed by make rules.

.gitignore
Makefile [new file with mode: 0644]
foto/album.inc.php [new file with mode: 0644]
foto/index.php [new file with mode: 0644]
foto/template.html [new file with mode: 0644]
lib/photoswipe.html [new file with mode: 0644]

index a99a8c95dbb773fabbb7b347f47b7c16aa2a5b57..33ea8fddd301c57d366d46468e8ff7a7901252fe 100644 (file)
@@ -1,5 +1,6 @@
-# link to some ckeditor build (or checkout in a pinch)
-/ckeditor
+# assets built by make rules
+/lib
+!/lib/photoswipe.html
 
 # dynamic user data
 /profile
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..49bf4d2
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,23 @@
+foto: lib/album.js lib/photoswipe.css lib/photoswipe-ui
+
+lib/album.js: lib/.photoswipe/dist/photoswipe.min.js lib/.photoswipe/dist/photoswipe-ui-default.min.js lib/.justified/dist/justified-layout.js
+       cat $^ >$@
+
+lib/photoswipe.css: lib/.photoswipe/dist/photoswipe.css
+       ln -sf ../$^ $@
+
+lib/photoswipe-ui: lib/.photoswipe/dist/default-skin
+       ln -sf ../$^ $@
+
+lib/.photoswipe/dist/photoswipe.css: lib/.photoswipe
+lib/.photoswipe/dist/photoswipe.min.js: lib/.photoswipe
+lib/.photoswipe/dist/default-skin: lib/.photoswipe
+
+lib/.photoswipe:
+       git clone https://github.com/dimsemenov/PhotoSwipe $@
+
+lib/.justified/dist/justified-layout.js: lib/.justified
+
+lib/.justified:
+       git clone https://github.com/flickr/justified-layout $@
+
diff --git a/foto/album.inc.php b/foto/album.inc.php
new file mode 100644 (file)
index 0000000..597e9a4
--- /dev/null
@@ -0,0 +1,100 @@
+<script src="/lib/album.js"></script>
+<link rel="stylesheet" href="/lib/photoswipe.css">
+<link rel="stylesheet" href="/lib/photoswipe-ui/default-skin.css">
+
+<?php
+include 'lib/photoswipe.html';
+?>
+
+<script>
+var pswpElement = document.querySelectorAll('.pswp')[0]
+var images = [];
+
+function openphotoswipe(index) {
+       var options = {
+               index: index,
+               loop: false,
+               getThumbBoundsFn: function(index) {
+                       var thumbpos = images[index].el.getBoundingClientRect();
+                       var pageYScroll = window.pageYOffset || document.documentElement.scrollTop;
+                       return { x:thumbpos.left, y:thumbpos.top + pageYScroll, w:thumbpos.width };
+               },
+               tapToClose: false,
+               clickToCloseNonZoomable: false,
+               closeElClasses: [], 
+               shareButtons: [
+                       {id:'download', label:'Origineel downloaden', url:'{{raw_image_url}}', download:true}
+               ],
+       };
+       var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, images, options);
+       gallery.init();
+       return false;
+}
+
+var gallery = document.querySelector('.album');
+var images = [];
+var ratios = [];
+
+var imgquery = gallery.querySelectorAll('img');
+for (i = 0; i < imgquery.length; i++) {
+       (function () {
+               var img = imgquery[i];
+               var index = images.length;
+               var size = img.getAttribute('data-size');
+               size = size ? size.split('x') : [img.width * 5, img.height * 5];
+               var item = {
+                       src: img.parentNode.getAttribute('href'),
+                       msrc: img.getAttribute('src'),
+                       w: size[0],
+                       h: size[1],
+                       el: img,
+                       pid: img.parentNode.getAttribute('href'),
+                       title: img.getAttribute('title'),
+               };
+               images.push(item);
+               img.onclick = function () { return openphotoswipe(index) };
+               ratios.push(size[0] / size[1]);
+       })();
+}
+
+function imgjustify() {
+       var csspad = window.getComputedStyle(gallery.children[0]).getPropertyValue('padding-right');
+       var pad = csspad && parseFloat(csspad.replace(/px$/, '')) || 4.5;
+       var config = {
+               containerWidth: gallery.offsetWidth,
+               containerPadding: pad,
+               boxSpacing: pad * 2,
+               targetRowHeight: 200,
+       };
+       var layout = require('justified-layout')(ratios, config);
+
+       gallery.style.position = 'relative';
+       gallery.style.height = layout.containerHeight + 'px';
+       for (i = 0; i < layout.boxes.length; i++) {
+               (function () {
+                       var imgel = images[i].el;
+                       imgel.style.width = layout.boxes[i].width + 'px';
+                       imgel.style.height = layout.boxes[i].height + 'px';
+                       imgel.style.position = 'absolute';
+                       imgel.style.top = layout.boxes[i].top + 'px';
+                       imgel.style.left = layout.boxes[i].left + 'px';
+
+                       var thumbtarget = imgel.src.replace(/(\/thumb\/)\d+/, '$1'+layout.boxes[i].height)
+                       if (imgel.complete) {
+                               imgel.src = thumbtarget;
+                       }
+                       else if (thumbtarget != imgel.src) {
+                               var loadthumb = new Image();
+                               loadthumb.src = thumbtarget;
+                               loadthumb.onload = function() {
+                                       imgel.src = this.src;
+                               };
+                       }
+               })();
+       }
+};
+
+window.addEventListener('resize', imgjustify, false);
+imgjustify();
+
+</script>
diff --git a/foto/index.php b/foto/index.php
new file mode 100644 (file)
index 0000000..eadda91
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+$intro = ob_get_clean();
+
+$rootdir = 'foto' . $Args;
+
+if (!empty($User['admin'])) {
+       $access = '🔓 Openbaar';
+       if (isset($PageAccess)) {
+               $access = "🔒 Bewoners";
+               if ($PageAccess != $Page.$Args) {
+                       $access .= sprintf(' vanaf <a href="%s">%s</a>',
+                               "/$PageAccess", pathinfo($PageAccess, PATHINFO_FILENAME)
+                       );
+               }
+       }
+       print "<aside>$access</aside>\n\n";
+}
+
+$nav = explode('/', $Page.$Args);
+$title = array_pop($nav);
+$rootname = "Foto's"; # override of 'foto'
+$link = '';
+print "<h2>";
+foreach ($nav as $i => $linktitle) {
+       $link .= "/$linktitle";
+       printf('<a href="%s">%s</a> â†’'."\n", $link, $i ? $linktitle : $rootname);
+}
+print $Args ? $title : $rootname;
+print "</h2>\n\n";
+
+print $intro;
+
+function showthumb($path)
+{
+       // assume all album entries are symlinks to archive originals
+       $target = preg_replace('{^(\.\./)*}', '', readlink($path));
+       $thumb = preg_replace('{^data/}', 'thumb/200/', $target);
+
+       @list ($order, $size, $title) = explode(':', pathinfo($path, PATHINFO_FILENAME), 3);
+       $imgtag = 'img src="/'.$thumb.'"';
+       if ($title) {
+               $imgtag .= ' title="'.htmlspecialchars(urldecode($title)).'"';
+       }
+       if ($size) {
+               $imgtag .= ' data-size="'.$size.'"';
+       }
+
+       return sprintf('<a href="/%s"><%s /></a>'."\n", $target, $imgtag);
+}
+
+if ($imgs = glob("$rootdir/*", GLOB_ONLYDIR)) {
+       natsort($imgs);
+       print '<ul class="gallery cat">'."\n";
+       foreach ($imgs as $path) {
+               $parts = pathinfo($path);
+               $album = $parts['filename'];
+               $cover = "$path/index.jpg";
+               if (!file_exists($cover)) $cover = 'foto/index.jpg';
+               if (is_link($cover)) {
+                       $cover = preg_replace('{^(?:\.\./)*data/}', 'thumb/100/', readlink($cover));
+               }
+
+               $html = '<img src="/'.$cover.'" />';
+               $html .= "<figcaption>$album</figcaption>";
+               if (empty($User) and file_exists("$path/.private")) {
+                       $html = '<strike title="bewoners">'.$html.'</strike>';
+               }
+               $html = "<figure>$html</figure>";
+
+               printf('<li id="%s"><a href="%s">%s</a>'."\n", $album, "/$path", $html);
+       }
+       print "</ul>\n\n";
+}
+
+if ($imgs = glob("$rootdir/*.jpg")) {
+       print '<ul class="gallery album">'."\n";
+       foreach ($imgs as $img) {
+               if ($img == "$rootdir/index.jpg") continue;
+               if (!is_link($img)) continue;
+               print '<li>'.showthumb($img);
+       }
+       print '</ul>'."\n\n";
+
+       include 'foto/album.inc.php';
+}
diff --git a/foto/template.html b/foto/template.html
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/photoswipe.html b/lib/photoswipe.html
new file mode 100644 (file)
index 0000000..6372c5b
--- /dev/null
@@ -0,0 +1,44 @@
+<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
+       <div class="pswp__bg"></div>
+
+       <div class="pswp__scroll-wrap">
+               <div class="pswp__container">
+                       <div class="pswp__item"></div>
+                       <div class="pswp__item"></div>
+                       <div class="pswp__item"></div>
+               </div>
+
+               <!-- PhotoSwipeUI_Default interface on top of sliding area -->
+               <div class="pswp__ui pswp__ui--hidden">
+
+                       <div class="pswp__top-bar">
+                               <div class="pswp__counter"></div>
+
+                               <button class="pswp__button pswp__button--close" title="Terug (Esc)"></button>
+                               <button class="pswp__button pswp__button--share" title="Opties"></button>
+                               <button class="pswp__button pswp__button--fs" title="Volledig scherm"></button>
+                               <button class="pswp__button pswp__button--zoom" title="Zoom in/uit"></button>
+
+                               <div class="pswp__preloader">
+                                       <div class="pswp__preloader__icn">
+                                               <div class="pswp__preloader__cut">
+                                                       <div class="pswp__preloader__donut"></div>
+                                               </div>
+                                       </div>
+                               </div>
+                       </div>
+
+                       <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
+                               <div class="pswp__share-tooltip"></div> 
+                       </div>
+
+                       <button class="pswp__button pswp__button--arrow--left" title="Vorige"></button>
+                       <button class="pswp__button pswp__button--arrow--right" title="Volgende"></button>
+
+                       <div class="pswp__caption">
+                               <div class="pswp__caption__center"></div>
+                       </div>
+
+               </div>
+       </div>
+</div>