view mgallery.php @ 109:c8cfc6cc161a

Adjust image scaling to be delayed and not being done on each resize event (for example Firefox fullscreen switching animates by default and triggers LOTS of resize events, which makes things slow.)
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 30 Oct 2016 15:22:03 +0200
parents 71de97240799
children 9da8bab49711
line wrap: on
line source

<?php
//
// Yet Another Image Gallery
// -- Main PHP file
// Programmed and designed by Matti 'ccr' Hamalainen <ccr@tnsp.org>
// (C) Copyright 2015-2016 Tecnic Software productions (TNSP)
//
require "mgallery.inc.php";

$pageLang = "fi";
$pageLanguages =
[
  "fi" => "FIN",
  "en" => "ENG",
];


$pageTranslations =
[
  "gallery_error" => [
    "fi" => "Tapahtui virhe",
    "en" => "An error occured",
  ],

  "invalid_gallery_path" => [
    "fi" => "Gallerian tiedostopolkua <b>%1</b> ei ole olemassa.",
    "en" => "Gallery path <b>%1</b> does not exist or is invalid.",
  ],

  "view_best_quality" => [
    "fi" => "Näytä parhaalla laadulla",
    "en" => "View best quality version",
  ],
];


//
// Various utility functions
//
function mgSetCookie($name, $value)
{
  global $galBaseURL;
  setcookie($name, $value, time() + 365*24*60*60, $galBaseURL); // expire in a year
}


function mgQM($msg)
{
  global $pageTranslations, $pageLang;

  if (isset($pageTranslations[$msg]) && isset($pageTranslations[$msg][$pageLang]))
    $str = $pageTranslations[$msg][$pageLang];
  else
    $str = $msg;

  foreach (func_get_args() as $argn => $argv)
    $str = preg_replace("/\%".$argn."/", $argv, $str);
  return $str;
}


function mgTrimIfString($val)
{
  if (is_string($val))
    return trim($val);
  else
    return $val;
}


function mgGetRequestItem($name, $default = "", $allowGet = FALSE)
{
  if ($allowGet)
    return isset($_REQUEST[$name]) ? mgTrimIfString($_REQUEST[$name]) : $default;
  else
    return isset($_POST[$name]) ? mgTrimIfString($_POST[$name]) : $default;
}


function chentities($str)
{
  return htmlentities($str, ENT_NOQUOTES, "UTF-8");
}


function mgPrintCSSLine($filename, $media = FALSE)
{
  echo
    "  <link rel=\"stylesheet\" href=\"".$filename.
    "\" type=\"text/css\" ".($media !== FALSE ? "media=\"".$media."\"": "")." />\n";
}


function mgPrintPageHeader($pageTitle, $pageExtra = "")
{
  global $pageCSS, $pageCSSIndex, $pageCharset, $pageAuthor, $pageCSSData, $pageUrchin;

  if (!isset($pageCSS))
  {
    if (!isset($pageCSSData))
    {
      $pageCSSData = array(
        "cookie" => "docscss",
        "prefix" => "/css/docs",
      );
    }

    if (isset($_GET["css"]))
    {
      $pageCSSIndex = intval($_GET["css"]);
      mgSetCookie($pageCSSData["cookie"], $pageCSSIndex); // expire in a year
    }
    else
    {
      $pageCSSIndex = isset($_COOKIE[$pageCSSData["cookie"]]) ? intval($_COOKIE[$pageCSSData["cookie"]]) : 1;
    }
    $pageCSS = $pageCSSData["prefix"].$pageCSSIndex.".css";
  }

  echo
  "<!DOCTYPE html>\n".
  "<html>\n".
  "<head>\n".
  "  <meta charset=\"".$pageCharset."\">\n".
  "  <meta http-equiv=\"Content-type\" content=\"text/html;charset=".$pageCharset."\">\n".
  "  <title>".strip_tags($pageTitle)."</title>\n".
  $pageExtra;

  if (is_array($pageCSS))
  {
    foreach ($pageCSS as $uri => $media)
      mgPrintCSSLine($uri, $media);
  }
  else
  {
    mgPrintCSSLine($pageCSS, FALSE);
  }

  echo
  "</head>\n".
  "<body>\n";

  if ($pageUrchin !== FALSE && file_exists($pageUrchin))
    require_once $pageUrchin;

  echo
    "<div id=\"messageBox\"></div>\n".
    "<div id=\"contents\">\n";
}


function mgPrintPageFooter()
{
  echo
    mgGetPageInfoFooter().
    "</div>\n".
    "</body>\n</html>\n";
}


function mgGetImageTitleStr($filename, &$data)
{
  return
    chentities($filename).
    mgGetArr($data, "datetime", " - %1", "", "mgTimeStr");
}


function mgGetImageURL()
{
  global $galImageURL, $galPath;
  return str_replace("//", "/", $galImageURL.$galPath."/".implode("", func_get_args()));
}


function mgGetURL($path, $image, $entities = TRUE)
{
  global $galBaseURL, $galCleanURLS;
  $amp = $entities ? "&amp;" : "&";

  if ($galCleanURLS)
  {
    $url = $galBaseURL.$path."/".($image !== FALSE ? $image : "");
  }
  else
  {
    $url =
      $galBaseURL.mgGetSetting("mgallery_php")."?path=".
      $path.($image !== FALSE ? $amp."image=".$image : "");
  }

  return str_replace("//", "/", $url);
}


function mgGetLanguageSelector()
{
  global $pageLang, $pageLanguages;
  if (isset($pageLanguages) && count($pageLanguages) > 0)
  {
    $str = "<div id=\"languages\">";
    foreach ($pageLanguages as $id => $lang)
      $str .= "<a href=\"?lang=".$id."\">".$lang."</a> ";
    return $str."</div>\n";
  }
  else
    return "";
}


function mgGetNaviActive(&$galIndex, $index, $delta, &$res, &$url, $entities)
{
  global $galPath;
  $res = $index + $delta;
  if ($res >= 0 && $res <= sizeof($galIndex) - 1)
  {
    $url = mgGetURL($galPath, $galIndex[$res], $entities);
    return TRUE;
  }
  else
    return FALSE;
}


function mgGetNaviControlImage(&$galIndex, $index, $class, $url)
{
  global $galTNPath;

  if ($url !== FALSE)
  {
    return
      "<div class=\"imageCtrl ".$class."\">".
      "<a href=\"".$url."\"><img src=\"".
      mgGetImageURL($galTNPath, $galIndex[$index]).
      "\" alt=\"".$galIndex[$index]."\" /></a></div>";
  }
  else
    return "";
}


function mgGetNaviControlImageBox(&$galIndex, $index, $class, $delta)
{
  if (!mgGetNaviActive($galIndex, $index, $delta, $res, $url, TRUE))
    $url = FALSE;

  return mgGetNaviControlImage($galIndex, $res, $class, $url);
}


function mgGetNaviControlImageBoxJS(&$galIndex, $index, $class, $delta)
{
  return
    "<script type=\"text/javascript\">".
    "document.write(\"".str_replace("\"", "\\\"",
    mgGetNaviControlImageBox($galIndex, $index, $class, $delta)).
    "\");".
    "</script>";
}


function mgPrintTable($class, &$galEntries, &$galIndex, $start, $limit)
{
  global $galAlbumIcon, $galPath, $galTNPath, $galImageURL, $galUseCoverImages, $galUseTables;

  $galCount = count($galIndex);
  if ($start >= $galCount)
    return $start;

  $end = ($limit === FALSE) ? $galCount : $start + $limit;
  if ($end > $galCount) $end = $galCount;

  $rowLimit = mgGetSetting("album_row_limit");
  $n = 0;

  if ($galUseTables)
    echo "<table class=\"galleryTable ".$class."\">\n";
  else
    echo "<div class=\"galleryTable ".$class."\">\n";

  for ($index = $start; $index < $end; $index++)
  {
    $filename = &$galIndex[$index];
    $data = &$galEntries[$filename];

    if ($galUseTables && $n == 0) echo " <tr>\n";

    if ($galUseTables)
      echo "  <td class=\"galleryEntry\" id=\"cd".$data["base"]."\">\n";
    else
      echo "  <div class=\"galleryEntry\">\n";

    if ($data["type"] == 0)
    {
      echo
        "    <div class=\"imageBox\" title=\"".mgGetImageTitleStr($filename, $data).
        "\"><a href=\"".mgGetURL($galPath, $filename)."\">".
        "<img src=\"".mgGetImageURL($galTNPath, $filename)."\" alt=\"".
        chentities($filename)."\"></a></div>\n".
        mgGetArr($data, "caption", "    <div class=\"imageCaption\">%1</div>\n", "", "chentities");
/*
      if ($mode == "")
      {
      echo
        "  <select class=\"dropdown\" id=\"dd".$data["base"]."\" name=\"dd".$data["base"].
        "\" onchange=\"galPhotoDataChanged('".$data["base"]."');\">\n";

      foreach ($picChoices as $name => $value)
      {
        echo "   <option value=\"$value\"".($value == $data["id"] ? " selected=\"selected\"" : "").">".chentities($name)."</option>\n";
      }
      echo
        "  </select>\n";
      }
*/
    }
    else
    {
      if ($galUseCoverImages && isset($data["image"]))
      {
        // Recursively determine the album cover image
        $ptmp = [$galPath, $filename];
        $curr = &$data;
        while (is_array($curr["image"]))
        {
          $curr = &$curr["image"];
          $ptmp[] = $curr["base"];
        }

        $ptmp[] = $galTNPath;
        $ptmp[] = $curr["image"];

        $atmp = $galImageURL.implode("/", mgCleanPathArray(TRUE, 0, count($ptmp), $ptmp));
      }
      else
      // No album cover, use default
        $atmp = $galAlbumIcon;

      echo
      "    <a href=\"".mgGetURL(mgCleanPath(TRUE, $galPath, $filename), FALSE)."\">".
      "<img class=\"albumIcon\" src=\"".$atmp."\" alt=\"".
      mgGetTrans($data["caption"], TRUE).
      "\" />\n".
      "    <div class=\"albumTitle\">".mgGetTrans($data["caption"], TRUE)."</div></a>\n";
    }

    if ($galUseTables)
    {
      echo "  </td>\n";
      if (++$n >= $rowLimit)
      {
        echo " </tr>\n";
        $n = 0;
      }
    }
    else
      echo "  </div>\n";
  }

  if ($galUseTables)
  {
    if ($n > 0)
    {
      while ($n++ < $rowLimit)
        echo "  <td></td>\n";
      echo " </tr>\n";
    }
    echo "</table>\n";
  }
  else
    echo "</div>\n";

  return $index;
}


function mgTimeStr($str)
{
  $tmp = date_create_from_format("Y:m:d H:i:s", $str);
  return date_format($tmp, "d M Y (H:i:s)");
}


function mgFileSize($size)
{
  foreach ([1024*1024 => "MiB", 1024 => "kiB"] as $mdiv => $mpost)
  {
    if ($size > $mdiv)
      return sprintf("%1.2f %s", $size / $mdiv, $mpost);
  }
  return sprintf("%d bytes", $size);
}


function mgGetPageInfoHeaderStart()
{
  return "<div id=\"pageInfoHeader\">\n";
}


function mgGetPageInfoHeaderEnd()
{
  return "</div>";
}


function mgGetPageInfoFooter()
{
  if (($str = mgGetSetting("page_info")) !== FALSE)
    return "<div id=\"pageInfoFooter\"><div>".$str."</div></div>";
  else
    return "";
}


function mgGetBreadCrump($class, &$pdata)
{
  return
    "<a class=\"".$class."\" href=\"".mgGetURL($pdata["path"], FALSE)."\">".
    mgGetTrans($pdata["caption"], TRUE).
    "</a>";
}


function mgPrintBreadCrumbs($galData)
{
  $res = [];
  if ($galData["caption"])
    $res[] = mgGetBreadCrump("naviBreadCrumbCurr", $galData);

  $tmp = $galData;
  while (isset($tmp["parent"]))
  {
    $pdata = $tmp["parent"];
    $res[] = mgGetBreadCrump("naviBreadCrumbItem", $pdata);
    $tmp = $tmp["parent"];
  }

  if (count($res) > 1)
  {
    echo
      "<div class=\"naviBreadCrumbs\">".
      implode("<span class=\"naviBreadCrumbSep\"></span>", array_reverse($res)).
      "</div>\n";
  }
}


//
// Global locale initialization
//
if (!isset($localeInited) || !$localeInited)
{
  $localeInited = TRUE;
  $pageCharset = "UTF-8";

  mb_internal_encoding($pageCharset);

  $tmp = "en_US.".strtolower(str_replace("-", "", $pageCharset));
  setlocale(LC_ALL, $tmp);
}


//
// Read and process gallery settings
//
mgReadSettings();

$pageCSS = mgGetSetting("css");
$pageCSSSelect = mgGetSetting("css_select");
$pageUrchin = mgGetSetting("urchin_file");
$galBasePath = mgGetSetting("base_path");
$galBaseURL = mgGetSetting("base_url");
$galImageURL = mgGetSetting("image_url", mgGetSetting("base_url"));

$galUseCoverImages = mgGetSetting("cover_images");
$galUseTables = mgGetSetting("use_tables");

$galAlbumIcon = mgGetSetting("album_icon");
$galCleanURLS = mgGetSetting("clean_urls");
$galTNPath = mgGetSetting("tn_path");
$galMedSuffix = mgGetSetting("med_suffix");
$galTitlePrefix = mgGetSetting("title_prefix");
$galTitleSep = mgGetSetting("title_sep");

$galMode = mgGetRequestItem("mode", "view", TRUE);
$galPath = mgGetRequestItem("path", ".", TRUE);
$galPageIndex = intval(mgGetRequestItem("index", 0, TRUE));
$galImage = mgGetRequestItem("image", FALSE, TRUE);

if (is_string($galImage))
  $galImage = basename($galImage);


// Check language setting
if (($tmp = mgGetRequestItem("lang", FALSE, TRUE)) !== FALSE)
{
  $tmp = strtolower($tmp);
  if (array_key_exists($tmp, $pageLanguages))
  {
    $pageLang = $tmp;
    mgSetCookie("mglang", $tmp);
  }
}
else
if (isset($_COOKIE["mglang"]))
{
  $tmp = strtolower($_COOKIE["mglang"]);
  if (array_key_exists($tmp, $pageLanguages))
    $pageLang = $tmp;
}


// Check "tables" setting
if (isset($_REQUEST["tables"]))
{
  $galUseTables = mgGetRequestItem("tables", FALSE, TRUE);
  mgSetCookie("mgtables", $galUseTables ? 1 : 0);
}
else
if (isset($_COOKIE["mgtables"]))
  $galUseTables = intval($_COOKIE["mgtables"]);


//
// Attempt to read the data cache file
//
$filename = mgGetPath(mgCleanPath(TRUE, $galBasePath, $galPath), "cache_file");
$filename2 = mgGetPath(mgCleanPath(FALSE, $galBasePath, $galPath), "cache_file");
if ($filename == $filename2 && file_exists($filename) && ($fp = @fopen($filename, "rb")) !== FALSE)
{
  if (flock($fp, LOCK_SH))
  {
    require($filename);
    flock($fp, LOCK_UN);
  }
  fclose($fp);
}


// If no data available, show an error page
if (!isset($galData) || !isset($galEntries) ||
    !isset($galAlbumsIndex) || !isset($galImagesIndex))
{
  mgPrintPageHeader(mgGetVal(["title_prefix", "title_sep"], "%1%2")."ERROR!");

  echo
    "<h1>".mgQM("gallery_error")."</h1>\n".
    "<p>".mgQM("invalid_gallery_path", chentities($galPath))."</p>";

  //echo "<p>filename=: '".$filename."', X: galData=".intval(isset($galData)).", galEntries=".intval(isset($galEntries))."\n";

  mgPrintPageFooter();
  exit;
}


//
// Print page header, etc.
//
if (($index = array_search($galImage, $galImagesIndex)) !== FALSE)
{
  //
  // Single image mode
  //
  $ctrlFlags = mgGetSetting("image_flags");
  $data = $galEntries[$galImage];

  $list = [
    mgGetArr($data, ["width", "height"], "<span class=\"infoDimensions\"><b>%1</b> x <b>%2</b> px</span>", NULL),
    mgGetArr($data, "model", "<span class=\"infoModel\"><b>%1</b></span>", NULL),
    mgGetArr($data, "fnumber", "<span class=\"infoFNumber\"><b>f/%1</b></span>", NULL),
    mgGetArr($data, "exposure", "<span class=\"infoExposure\"><b>%1</b> sec</span>", NULL, NULL),
    mgGetArr($data, "iso", "<span class=\"infoISO\">ISO <b>%1</b></span>", NULL),
    mgGetArr($data, "focallength", "<span class=\"infoFocalLength\">F-L <b>%1</b>mm</span>", NULL),
    mgGetArr($data, "lensmodel", "<span class=\"infoLensModel\"><i>%1</i></span>", NULL),
    mgGetArr($data, "filesize", "<span class=\"infoFileSize\">%1</span>", "", "mgFileSize"),
  ];

  $pageTitle = $galTitlePrefix.$galTitleSep.mgGetTrans($galData["caption"])." - ".$galImage;
  mgPrintPageHeader($pageTitle);
  echo
    mgGetPageInfoHeaderStart().
    mgGetLanguageSelector().
    "<h1>".chentities($pageTitle)."</h1>\n";

  if ($ctrlFlags & MGF_BREADCRUMBS)
    mgPrintBreadCrumbs($galData);

  echo
    mgGetPageInfoHeaderEnd().
    "<div id=\"imageCBox\">\n".
    mgGetNaviControlImageBoxJS($galImagesIndex, $index, "prevBtm", -1)."\n".
    mgGetNaviControlImageBox($galImagesIndex, $index, "prev", -1)."\n".
    "<div class=\"imageBox\">\n".
    "<img id=\"imageImage\" src=\"".mgGetImageURL($galTNPath, $data["base"].$galMedSuffix.$data["ext"])."\" alt=\"".chentities($data["base"].$galMedSuffix.$data["ext"])."\">".
    "</div>\n".
    mgGetNaviControlImageBoxJS($galImagesIndex, $index, "nextBtm", 1)."\n".
    mgGetNaviControlImageBox($galImagesIndex, $index, "next", 1)."\n".
    "<div class=\"imageCaption\">".mgGetArr($data, "caption", "%1", "")."</div>\n".
    "<div class=\"infoBox\">\n".
    mgGetArr($data, "datetime", "<span class=\"infoDateTime\">%1</span>", "", "mgTimeStr").
    implode(", ", array_filter($list, function($a) { return $a !== NULL; })).
    "<a id=\"imageLink\" target=\"_blank\" href=\"".$galImageURL.$galPath."/".$galImage."\">".
    mgQM("view_best_quality")."</a>\n".
    "</div>".
    "</div>\n";

  // Javascript navigation
  if ($ctrlFlags & MGF_JAVASCRIPT)
  {
    $prevActive = mgGetNaviActive($galImagesIndex, $index, -1, $res, $prevURL, FALSE);
    $nextActive = mgGetNaviActive($galImagesIndex, $index, 1, $res, $nextURL, FALSE);
    echo
      "<script type=\"text/javascript\">\n".
      "var mgalPrevURL = \"".($prevActive ? $prevURL : "")."\";\n".
      "var mgalNextURL = \"".($nextActive ? $nextURL : "")."\";\n".
      "var mgalUpURL = \"".mgGetURL($galData["path"], FALSE)."\";\n".
      "\n";

?>
function mgalAddEvent(evobj, evtype, evcallback)
{
  if (evobj == null || typeof(evobj) == 'undefined')
    return;

  if (evobj.addEventListener)
    evobj.addEventListener(evtype, evcallback, false);
  else
  if (evobj.attachEvent)
    evobj.attachEvent("on" + evtype, evcallback);
  else
    evobj["on"+evtype] = evcallback;
};


function mgalNavigateTo(url)
{
  if (url != "")
    window.location = url;
}


function mgalProcessKeyPress(ev)
{
  ev = ev || window.event;
  var key = ev.keyCode ? ev.keyCode : ev.which;
  switch (key)
  {
    case 37:
    case 65:
    case 52:
      // left
      mgalNavigateTo(mgalPrevURL);
      break;

    case 39:
    case 68:
    case 54:
      // right
      mgalNavigateTo(mgalNextURL);
      break;

    case 38:
    case 56:
      // up
      mgalNavigateTo(mgalUpURL);
      break;

    default:
      return true;
  }

  ev.preventDefault();
  return false;
}


function mgalGetWindowSize()
{
  var winW = 0, winH = 0;
  if (typeof(window.innerWidth) == 'number')
  {
    // Non-MSIE
    winW = window.innerWidth;
    winH = window.innerHeight;
  }
  else
  if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight))
  {
    // MSIE 6+ in 'standards compliant mode'
    winW = document.documentElement.clientWidth;
    winH = document.documentElement.clientHeight;
  }
  else
  if (document.body && (document.body.clientWidth || document.body.clientHeight))
  {
    // MSIE 4 compatible
    winW = document.body.clientWidth;
    winH = document.body.clientHeight;
  }

  return [winW, winH];
}


function mgalAdjustImageDo()
{
  var eimg = document.getElementById("imageImage");
  var win = mgalGetWindowSize();
  var madj = 0.81;
  if (eimg)
  {
    if (eimg.width > eimg.height)
    {
      eimg.style.width = "100%";
      eimg.style.height = "auto";
      if (eimg.height > win[1] * madj)
      {
        eimg.style.width = "auto";
        eimg.style.height = (win[1] * madj)+"px";
      }
    }
    else
    {
      eimg.style.width = "auto";
      if (eimg.height > win[1] * madj)
        eimg.style.height = (win[1] * madj)+"px";
      else
        eimg.style.height = "100%";
    }
  }
  adjustPID = -1;
}


function mgalAdjustImage()
{
  if (adjustPID == -1)
    adjustPID = setTimeout(mgalAdjustImageDo, 500);
}


mgalAddEvent(document.getElementById("imageImage"), "load", mgalAdjustImageDo);
mgalAddEvent(window, "resize", mgalAdjustImage);
mgalAddEvent(document, "keypress", mgalProcessKeyPress);
adjustPID = -1;

<?
    echo
      "</script>\n";
  }
}
else
{
  //
  // Gallery mode
  //
  // - needs sub-modes / handling of order shit
  // - Javascript stuff for picture data updates
  //
  $pageTitle = $galTitlePrefix.mgGetArr($galData, "caption", " - %1", "", "chentities");
  mgPrintPageHeader($pageTitle);
  echo
    mgGetPageInfoHeaderStart().
    mgGetLanguageSelector().
    "<h1>".$pageTitle."</h1>\n";

  $ctrlFlags = mgGetSetting("album_flags");
  if ($ctrlFlags & MGF_BREADCRUMBS)
    mgPrintBreadCrumbs($galData);

  if (isset($galData["header"]) && strlen($gheader = mgGetTrans($galData["header"])) > 0)
  {
    // Translate relative URLs in header, if needed
    $baseURL = mgGetSetting("image_url");

    $headerText = preg_replace_callback(
      "@href\s*=\s*\"([^\"]+)\"@i",
      function ($matches) use($galPath, $baseURL)
      {
        $mstmp = $matches[1];
        if (preg_match("@^[a-z]+://@i", $mstmp) === 0)
        {
          if ($mstmp[0] != "/")
            $mstmp = $baseURL."/".$galPath."/".str_replace("//", "/", $mstmp);
        }
        return "href=\"".$mstmp."\"";
      },
      $gheader);

    echo "<div class=\"albumHeaderText\">".$headerText."</div>\n";
  }

  echo mgGetPageInfoHeaderEnd();

  mgPrintTable("albumTable", $galEntries, $galAlbumsIndex, 0, FALSE);
  mgPrintTable("imageTable", $galEntries, $galImagesIndex, 0, FALSE);
}

mgPrintPageFooter();
?>