/*
 *--------------------------------------------------------------------------------------
 *
 *  R e s p o n s i v e   W W W - S l i d e s h o w
 *
 *  made with HTML5, CSS3, and Vanilla JavaScript
 *
 *  ===> Highlight:  use image-metadata for title and caption :-)
 *
 *  Idea from:            https://github.com/leemark/better-simple-slideshow   (1)
 *  Swipe-Support from:   https://hammerjs.github.io/api/                      (2)
 *  Symbol-Icons from:    https://fontawesome.github.io/Font-Awesome/icons/    (2)
 *  EXIF-metadata from:   https://github.com/exif-js/exif-js                   (1)
 *
 *  (1) substantially modified and extended
 *  (2) unmodified
 *
 *  UTF16 special characters:   https://www.tamasoft.co.jp/en/general-info/unicode.html
 *
 *  Modifications and extensions by:          peter@danninger.eu         14.12.2023
 *
 *--------------------------------------------------------------------------------------
 *
 *  Local tests only possible with restrictions:
 *  Problems with the extraction of image metadata and displaying the icons.
 *  depending on the browser.
 *
 *  Problem: "cross-origin-request-error"
 *
 *--------------------------------------------------------------------------------------
 */

//----------------------------------------------------------------------------
var slideShow = slideShow || {};                // Namespace
//----------------------------------------------------------------------------
slideShow._copyright = "peter@danninger.eu";    // Copyright
slideShow._version = "14.12.2023";              // Version
//----------------------------------------------------------------------------
slideShow.loc = window.location.pathname;
slideShow.bigSpinner = false;                   // bigSpinner instead of spinner
slideShow.bgColor = "#333333";                  // because of bigSpinner with black background
slideShow.useThumbs = false;                    // thumbnails instead of spinner, not used actually
slideShow.enlarge = false;                      // actual image displayed enlarged
slideShow.fullStat = false;                     // change to fullScreen mode
//
slideShow.makeShow = function (classNam, opts) {
  //
  // if you change the defaults, you also have to modify slideshow.css !
  //
  var mainDiv = (typeof classNam.mainDiv == "undefined") ? "slideShow" : classNam.mainDiv;          // main div class
  var slidesDiv = (typeof classNam.slidesDiv == "undefined") ? "slides" : classNam.slidesDiv;       // slides div class
  var pageTitle = (typeof classNam.pageTitle == "undefined") ? "pageTitle" : classNam.pageTitle;    // pageTitle div class
  var figTitle = (typeof classNam.figTitle == "undefined") ? "figTitle" : classNam.figTitle;        // figTitle div class
  var helpMeta = (typeof classNam.helpMeta == "undefined") ? "helpMeta" : classNam.helpMeta;        // helpMeta div class
  //
  // I support only one slideShow
  //
  //var $slideshows = document.querySelectorAll ("." + slidesDiv);    // a collection of all slideshows
  //
  var $slideshow = {};
  //
  // the slideshow class / object
  //
  var Slide_Show = {
    init: function (slidesEl, opts) {
      this.traces = {                                       // activate trace alert's
        keyboard: false,                                      // key codes
        fullscreen: false,                                    // fullscreen returns
        picdir: false,                                        // picdir test
        meta: false,                                          // metadata test
        random: false,                                        // random mode test
        swipe: false                                          // swipe test
      }
      this.wait = false;                                    // limit keyboard events
      this.counter = 0;                                     // current image
      this.oldcnt = -1;                                     // last recent image
      this.oldcnt2 = -1;                                    // second last recent image
      this.loaded = false;                                  // first image loaded (remove spinner)
      this.slidesEl = slidesEl;                             // current slideshow container
      this.$items = slidesEl.querySelectorAll ('figure');   // a collection of all of the images
      this.numItems = this.$items.length;                   // total number of images
      //
      // icon classes and tooltip strings
      //
      this.icons = {
        pre: "fa ",
        //
        symPrev:      "fa-backward",
        tipPrev:      strings.tipPrev,            // to previous image
        //
        symNext:      "fa-forward",
        tipNext:      strings.tipNext,            // to next image
        //
        symFirst:     "fa-fast-backward",
        tipFirst:     strings.tipFirst,           // to first image
        //
        symDownload:  "fa-download",
        tipDownload:  strings.tipDownload,        // download image
        //
        symPrint:     "fa-print",
        tipPrint:     strings.tipPrint,           // print image
        //
        symExit:      "fa-external-link",
        tipExit:      strings.tipExit,            // exit slideshow
        //
        symFull:      "fa-arrows-alt",
        tipFull:      strings.tipFull,            // fullscreen modus
        //
        symFullOff:   "fa-compress",
        tipFullOff:   strings.tipFullOff,         // exit fullscreen modus
        //
        symPlay:      "fa-youtube-play",
        tipPlay:      strings.tipPlay,            // play slideshow, modus: F1, F2
        //
        symPause:     "fa-pause",
        tipPause:     strings.tipPause,           // stop slideshow
        //
        symInfo:      "fa-camera",
        tipInfo:      strings.tipInfo,            // show image metadata
        //
        symMap:       "fa-map-marker",
        tipMap:       strings.tipMap,             // image location in Google Maps
        //
        symHelp:      "fa-question",              // slideshow informations
        tipHelp:      strings.tipHelp,
        //
      };
      //
      this.helpflag = false;                      // Helpmenue active
      this.metaflag = false;                      // Metatags active
      this.mapflag = false;                       // GoogleMaps active
      //
      // store the images filenames
      //
      this.nam = new Array ();                    // the image filenames
      this.imgs = new Array ();                   // the image src
      if (slideShow.useThumbs) {
        this.thumbs = new Array ();               // the thumbnail src
      }
      //
      // store the images metadata
      //
      this.meta = new Array ();                   // for every image a metadata object
      //
      // evaluate opts
      //
      opts = opts || {};                          // if opts object not passed in, then set to empty object
      this.opts = {
        scriptDir:      (typeof opts.scriptDir == "undefined" || opts.scriptDir == false) ? "" : opts.scriptDir,
        oriPicDir:      (typeof opts.oriPicDir == "undefined" || opts.oriPicDir == false) ? "" : opts.oriPicDir,
        picDir:         (typeof opts.picDir == "undefined" || opts.picDir == false) ? "" : opts.picDir,
        thumbDir:       (typeof opts.thumbDir == "undefined" || opts.thumbDir == false) ? "" : opts.thumbDir,
        enlarge:        (typeof opts.enlarge == "undefined") ? false : opts.enlarge,
        startNr:        (typeof opts.startNr == "undefined") ? 0 : opts.startNr,
        playPauseBut:   (typeof opts.playPauseBut == "undefined") ? false : opts.playPauseBut,
        speed:          (typeof opts.speed == "undefined") ? 2 : opts.speed,
        playMod:        (typeof opts.playMod == "undefined") ? 0 : opts.playMod,
        wrapAround:     (typeof opts.wrapAround == "undefined") ? false : opts.wrapAround,
        transition:     (typeof opts.transition == "undefined") ? false : opts.transition,
        waitProceed:    (typeof opts.waitProceed == "undefined") ? 1000 : opts.waitProceed * 1000,
        addTitle:       (typeof opts.addTitle == "undefined") ? false : opts.addTitle,
        TitLeft1:       (typeof opts.addTitle.TitLeft1 == "undefined") ? false : opts.addTitle.TitLeft1,
        TitLeft2:       (typeof opts.addTitle.TitLeft2 == "undefined") ? false : opts.addTitle.TitLeft2,
        TitRight1:      (typeof opts.addTitle.TitRight1 == "undefined") ? false : opts.addTitle.TitRight1,
        TitRight2:      (typeof opts.addTitle.TitRight2 == "undefined") ? false : opts.addTitle.TitRight2,
        addCaption:     (typeof opts.addCaption == "undefined") ? false : opts.addCaption,
        CapLeft1:       (typeof opts.addCaption.CapLeft1 == "undefined") ? false : opts.addCaption.CapLeft1,
        CapLeft2:       (typeof opts.addCaption.CapLeft2 == "undefined") ? false : opts.addCaption.CapLeft2,
        CapRight1:      (typeof opts.addCaption.CapRight1 == "undefined") ? false : opts.addCaption.CapRight1,
        CapRight2:      (typeof opts.addCaption.CapRight2 == "undefined") ? false : opts.addCaption.CapRight2,
        swipe:          (typeof opts.swipe == "undefined") ? false : opts.swipe,
        downloadBut:    (typeof opts.downloadBut == "undefined") ? false : opts.downloadBut,
        printBut:       (typeof opts.printBut == "undefined") ? false : opts.printBut,
        fullScreenBut:  (typeof opts.fullScreenBut == "undefined") ? false : opts.fullScreenBut,
        exitBut:        (typeof opts.exitBut == "undefined") ? false : opts.exitBut,
        exitURL:        (typeof opts.exitURL == "undefined") ? "" : opts.exitURL,
        metaDataBut:    (typeof opts.metaDataBut == "undefined") ? false : opts.metaDataBut,
        googleMapsBut:  (typeof opts.googleMapsBut == "undefined") ? false : opts.googleMapsBut,
        googleMapsMap:  (typeof opts.googleMapsMap == "undefined") ? "hybrid" : opts.googleMapsMap,
        googleMapsKey:  (typeof opts.googleMapsKey == "undefined") ? false : opts.googleMapsKey,
        helpBut:        (typeof opts.helpBut == "undefined") ? false : opts.helpBut,
      };
      //
      this.hsp = "\u00a0";                        // utf hardspace
      this.hsp2 = "\u00a0\u00a0";                 // 2 utf hardspaces
      this.hsp3 = "\u00a0\u00a0\u00a0";           // 3 utf hardspaces
      this.hsp5 = this.hsp2 + this.hsp3;          // 5 utf hardspaces
      this.hsp10 = this.hsp5 + this.hsp5;         // 10 utf hardspaces
      this.hsp20 = this.hsp10 + this.hsp10;       // 20 utf hardspaces
      this.cleft = "\u23f4";                      // cursor left symbol
      this.cright = "\u23f5";                     // cursor right symbol
      this.cup = "\u23f6";                        // cursor up symbol
      this.cdown = "\u23f7";                      // cursor down symbol
      this.copyright = "\u00a9";                  // copyright symbol
      this.degree = "\u00b0";                     // degree symbol
      //
      this.spinner = this.opts.scriptDir + "icons/spin0.gif";   // https://spiffygif.com/?color=858585&halo=true
      this.bigSpinner = this.opts.scriptDir + "icons/bigSpinner.gif";
      this.showSpinner (this.opts.startNr);                     // show spinner
      //
      // plausi checks
      //
      this.wrapAroundBak = this.opts.wrapAround;
      if (this.opts.playPauseBut) {
        if (this.opts.speed < 2) {
          this.opts.speed = 2;
        }
        if (this.opts.playMod < 0 || this.opts.playMod > 3) {
          this.opts.playMod = 0;
        }
      }
      if (typeof document.getElementsByClassName (mainDiv) != "object") {
        this.opts.fullScreenBut = false;                    // Safari
        this.opts.downloadBut = false;                      // Safari
      }
      if (this.opts.exitBut && this.opts.exitURL == "" && window.opener == null) {
        this.opts.exitBut = false;                          // ExitBut only if window.opener does exist
      }
      if (this.opts.googleMapsMap != "roadmap" && this.opts.googleMapsMap != "satellite" && 
          this.opts.googleMapsMap != "terrain" && this.opts.googleMapsMap != "hybrid") {
        this.opts.googleMapsMap = "hybrid";
      }
      this.interval = false;
      this.playing = false;
      this.playMod = this.opts.playMod;                     // default play mode: 0: forward, 1: backward, 2: random
      this.readMeta = true;
      if (!this.opts.addTitle && !this.opts.addCaption &&
          !this.opts.googleMapsBut && !this.opts.metaDataBut) {
        this.readMeta = false;
      }
      this.randpic = Math.floor (Math.random () * this.numItems);   // calculate random pic
      if (this.opts.swipe) {                                // swipe support
        this.addSwipe (this.slidesEl);
      }
      if (this.opts.waitProceed < 200)      this.opts.waitProceed = 200;
      if (this.opts.waitProceed > 2000)     this.opts.waitProceed = 2000;
      //
      this.transitionTime ();                               // set/reset transition time (Default: 1 sec)
      //
      this.injectControls (this.slidesEl);                  // show control icons
      this.addEventListeners (this.slidesEl);               // add event listeners
      //
      this.evalFigures ();                                  // add directory to img src
      //
      // actually startet after onload
      //
      // this.showCurrent (this.opts.startNr);                 // start show with first image
      //
      slideShow.resizeShow (classNam, false);               // optimize size, improve responsivness
      //
      window.setTimeout (function () {                      // no onload if image in browser cache, start nevertheless
        $slideshow.checkLoaded ();
      }, 2000);
    },

    //
    // ============================= functions ==================================================
    //
    // get an image object
    //
    getImage: function (n) {
      var that = this;
      var fig = that.$items[n];
      var imo = fig.getElementsByTagName ("img")[0];
      if (imo == null || (typeof imo != "object" && typeof imo != "function")) {
        if (that.traces.picdir)     alert (strings.imgErr + ": " + n);     // problem with image
        return (null);
      }
      return (imo);
    },
    //
    // workaround if onload missing (image already in browser cache)
    //
    checkLoaded: function () {
      var that = this;
        if (that.loaded) {
          return;
        } else {
          that.showCurrent (that.opts.startNr);   // start first image nevertheless
        }
    },
    //
    // show spinner after starting the show (slow network)
    //
    showSpinner: function (nr) {
      var that = this;
      var imo;
      if (nr < 0)             return;
      nr += 3;
      if (nr >= this.numItems)    nr -= 5;
      if (nr < 0)                 nr = 0;
      imo = that.getImage (nr);
      if (imo == null)        return;
      imo.src = that.spinner;                     // that.spinner or that.bigSpinner
      if (slideShow.bigSpinner) {
        imo.src = that.bigSpinner;
        document.body.style.backgroundColor = "#000000";
      }
      that.$items[nr].classList.add ('show');
    },
    //
    // add directory to img src, use "alt" to create "src"
    // we use "alt" in the HTML-file to prevent browsers to preload all the images
    //
    evalFigures: function () {
      var that = this;
      var i, j, imo, img, thumb, nam, alt, capt, txt;
      j = that.opts.picDir;
      if (j.indexOf ("http") != 0 && j.indexOf ("file") != 0 && j.indexOf ("./") != 0) {
        j = "./" + j;
      }
      if (j.charAt (j.length - 1) != '/') {
        j = j + "/";
      }
      for (i = 0; i < that.numItems; ++i) {                 // modify all images
        imo = that.getImage (i);
        if (imo == null)            continue;
        alt = imo.alt;
        j = alt.lastIndexOf ('/');
        nam = (j >= 0) ? alt.substring (j + 1) : alt;       // filename of image
        that.nam[i] = nam;
        if (that.opts.picDir != "") {                       // image directory
          img = that.opts.picDir + nam;
          that.imgs[i] = img;
        } else {
          alert (strings.slidesErr + " :-(");               // no images directory :-(
          exit;
        }
        if (slideShow.useThumbs) {
          if (that.opts.thumbDir != "") {                   // thumbnails directory
            thumb = that.opts.thumbDir + nam;
            that.thumbs[i] = thumb;
          } else {
            alert (strings.thumbsErr + " :-(");             // no thumbnails directory :-(
            exit;
          }
        }
        if (i == that.opts.startNr) {
          imo.onerror = function () {
            alert (strings.imgErr + ": " + that.opts.startNr);  // problem with image
            that.showCurrent (that.opts.startNr);               // start first image nevertheless
          }
          //
          // ====> start first image after onload ====>
          //
          imo.onload = function () {
            if (that.opts.startNr < 0)    return;
            that.loaded = true;
            that.showCurrent (that.opts.startNr);           // start show with first image
          }
          imo.src = img;
        }
        //
        // placeholder for title and caption to avoid fluckering
        //
        if (that.readMeta) {
          if (that.opts.addTitle)           that.addTit (false, i);
          if (that.opts.addCaption)         that.addCap (false, i);
        }
      }
    },
    //
    // next | previous | first | defined | random image | same | reload
    //
    // wrap around if option "this.opts.wrapAround"
    //
    // i == 0:  first image
    // i >= 1:  startNr, defined image
    // i == -1: same image
    // i == -2: next image
    // i == -3: previous image
    // i == -4: random image
    // i <= -5: reload image
    //
    // to load only active images proved to be important
    // when the show has more then 200 images.
    // Reason: extracting the metadata needs a lot of power.
    //
    showCurrent: function (i) {
      var that = this;
      var TitLeft1, TitLeft2, TitRight1, TitRight2;
      var CapLeft1, CapLeft2, CapRight1, CapRight2;
      var txt = "";
      var et = strings.imgErr + ": ";
      var imo, capt, txtn, spin;
      that.metaInfo (false);
      var wrap = that.opts.wrapAround;
      if (i > 1)        that.counter = 0;         // defined image (start image)
      if (i > that.numItems) {                    // unvalid parameter
        alert ("showCurrent: " + strings.unvalidPar + ": " + i);
        i = 0;
      }
      if (i == -4) {                              // random image
        if (that.randpic >= 0 && that.randpic < that.numItems) {
          i = that.randpic;                       // preloaded random image
        } else {                                  // should not happen ....
          i = Math.floor (Math.random () * that.numItems);
        }
        that.counter = 0;
        that.randpic = Math.floor (Math.random () * that.numItems);   // calculate next random image
        while (i == that.randpic || that.oldcnt == that.randpic) {
          that.randpic = Math.floor (Math.random () * that.numItems);
        }
        if (that.traces.random)     alert ("showCurrent: random: i: " + i + "   th_rp: " + that.randpic);
      }
      if (i == 0) {                               // first image
        that.counter = 0;
      } else if (i == -1) {                       // same image
      } else if (i == -2) {                       // next image
        that.counter = (that.counter + 1 >= that.numItems) ? ((wrap) ? 0 : that.numItems - 1) : that.counter + 1;
      } else if (i > 0) {                         // defined or random image
        //that.counter = (that.counter + i >= that.numItems) ? ((wrap) ? 0 : that.numItems - 1) : that.counter + i;
        that.counter = i;
      } else if (i == -3) {                       // previous image
        that.counter = (that.counter - 1 < 0) ? ((wrap) ? that.numItems - 1 : 0) : that.counter - 1;
      } else if (i == -5) {                       // reload the image
        if (isNaN (that.counter))     that.counter = 0;
        ++that.counter;
        window.location.href = slideShow.loc + "?startNr=" + that.counter.toString().padStart(3, '0');
      } else {
        alert ("wrong parameter: showCurrent (" + i + ")");
      }
      if (that.oldcnt == that.counter && that.oldcnt != that.opts.startNr) {    // no wrap-around at the show limits
        return;
      }
      //
      // big image to show the actual image, should be preloaded
      //
      if (isNaN (that.counter))     that.counter = 0;
      imo = that.getImage (that.counter);
      if (imo == null) {
        alert ("getImage error (null): " + that.counter);
        return;
      }
      imo.onerror = function () {
        alert (strings.imgErr + ": " + that.counter);
        return;
      }
      //
      // resize image onload if needed
      //
      imo.onload = function () {
        var img = that.getImage (that.counter);   // otherwise wrong image-size !!!
        if (img == null) {
          alert ("getImage error (null): " + that.counter);
          return;
        }
        if (img.width < 100 || img.height < 100) {
          img = that.getImage (that.counter);
          if (img == null) {
            alert ("getImage error (null): " + that.counter);
            return;
          }
        }
        var iwm, ihm, iwr, ihr, ptit;
        var iw = img.width;
        var ih = img.height;
        var ptith = 30;                 // default page-title-height at big screen
        var ftith = 30;                 // default image-title-height at big screen
        var caph = 30;                  // default image-caption-height at big screen
        var symw = 36;                  // default symbol-width at big screen
        var pad = 24;                   // padding reserve (empirisch)
        var smax = 1600;                // resize elements on lower screen-diagonales
        var smin = 600;                 //  .... but only until this lower limit
        var fact = fact1 = 1;           // resizing factors
        var swi = screen.width;
        var shg = screen.height;
        var wiw = window.innerWidth;
        var wih = window.innerHeight;
        var dcw = document.documentElement.clientWidth;
        var dch = document.documentElement.clientHeight;
        var wwi = (dcw > wiw) ? dcw : wiw;
        var whg = (dch > wih) ? dch : wih;
        if (wwi < 100 && swi > wwi)     wwi = swi;
        if (whg < 100 && shg > whg)     whg = shg;
        var diag = Math.floor (Math.sqrt ((wwi * wwi) + (whg * whg)));
        if (diag < smax) {
          if (diag < smin)              diag = smin;
          fact1 = diag / smax;          // not used actually
          fact = Math.sqrt (fact1);
          ptith = Math.floor (ptith * fact);
          ftith = Math.floor (ftith * fact);
          caph = Math.floor (caph * fact);
        }
        ptit = document.getElementsByClassName (pageTitle)[0];
        if (ptit == null || (typeof ptit != "object" && typeof ptit != "function")) {
          ptith = 0;                              // pageTitle is optional
        }
        iwm = wwi - symw - symw;                  // symbols left & right
        ihm = whg - ptith - ftith - caph;         // title & caption
        if (that.opts.enlarge) {                  // enlarge images on big displays
          if (iwm > iw) {
            ihr = Math.floor (ih * iwm / iw);
            iwr = iwm;
          }
          if (ihr > ihm) {
            iwr = Math.floor (iwm * ihm / ihr);
            ihr = ihm;
          }
          img.style.width = iwr - pad + "px";
          img.style.height = ihr - pad + "px";
          if ((iw < iwr || ih < ihr) && iw > 100 && ih > 100) {
            slideShow.enlarge = true;
          } else {
            slideShow.enlarge = false;
          }
        } else {
          img.style.maxWidth = iwm - pad + "px";
          img.style.maxHeight = ihm - pad + "px";
        }
      }
      imo.src = that.imgs[that.counter];          // load the image
      //
      // remove .show from the spinner
      //
      if ((that.opts.startNr) >= 0) {
        spin = that.opts.startNr + 3;
        if (spin >= that.numItems)    spin -= 5;
        if (spin < 0)                 spin = 0;
        document.body.style.backgroundColor = slideShow.bgColor;
        that.$items[spin].classList.remove ('show');
        that.opts.startNr = -99;
      }
      //
      // remove .show from the last recent item
      //
      if (that.oldcnt >= 0 && that.oldcnt != that.counter) {
        that.$items[that.oldcnt].classList.remove ('show');
      }
      //
      // add .show to the actual item
      //
      that.$items[that.counter].classList.add ('show');
      //
      // get image metadata and insert in <div class="figTitle"> and/or <figcaption>
      //
      // for better performance we extract only once and store the metadata
      // read only if needed for the show
      //
      if (that.readMeta) {
        if (typeof that.meta[that.counter] != "object") {       // image metadata not yet known
          if (that.traces.meta)           alert ("get new metadata: " + that.counter);
          imo = that.getImage (that.counter);
          if (imo == null)                return;
          if (!imo.complete) {                                  // Microsoft Edge problem
            window.setTimeout (function () {
              slideShow.getMetaTags (imo, function (meta) {     // callback function
                if (! meta) {
                  that.meta[that.counter] = false;
                  return;
                }
                that.meta[that.counter] = meta;                 // store the metadata
                if (that.opts.addTitle)       that.addTit (meta);
                if (that.opts.addCaption)     that.addCap (meta);
              });
            }, 300);
          }
          slideShow.getMetaTags (imo, function (meta) {         // callback function
            if (! meta) {
              that.meta[that.counter] = false;
              return;
            }
            that.meta[that.counter] = meta;                     // store the metadata
            if (that.opts.addTitle)       that.addTit (meta);
            if (that.opts.addCaption)     that.addCap (meta);
          });
        } else {                                                // image metadata already known
           if (that.traces.meta)          alert ("use stored metadata: " + that.counter);
           if (that.opts.addTitle)        that.addTit (that.meta[that.counter]);
           if (that.opts.addCaption)      that.addCap (that.meta[that.counter]);
        }
      }
      //
      // preload next image, previous image, first image and random image
      //
      if ((that.counter + 1) < that.numItems) {
        imo = that.getImage (that.counter + 1);
        if (imo == null) {
          alert ("preload-error, getImage: " + that.counter + 1);
          return;
        }
        imo.src = that.imgs[that.counter + 1];
      }
      if ((that.counter - 1) > 0) {
        imo = that.getImage (that.counter - 1);
        if (imo == null) {
          alert ("preload-error, getImage: " + that.counter - 1);
          return;
        }
        imo.src = that.imgs[that.counter - 1];
      }
      if (that.counter == 0 || that.counter == 1) {
        imo = that.getImage (that.counter);
        if (imo == null) {
          alert ("preload-error, getImage: " + that.counter);
          return;
        }
        imo.src = that.imgs[that.counter];
      }
      if (that.randpic >= 0) {
        imo = that.getImage (that.randpic);
        if (imo == null) {
          alert ("preload-error, getImage: " + that.randpic);
          return;
        }
        imo.src = that.imgs[that.randpic];
      }
      //
      // thumbnail or spinner for second last image, actually not used
      // don't alter the last image because of the transition
      //
      if (that.oldcnt2 > 0 && that.oldcnt2 != that.counter) {   // image 0 always big because of onresize
        imo = that.getImage (that.oldcnt2);
        if (imo == null)            return;
        if (slideShow.useThumbs) {
          imo.src = that.thumbs[that.oldcnt2];
          //imo.src = that.spinner;
        }
      }
      that.oldcnt2 = that.oldcnt;       // remember recent 2 images because of transition
      that.oldcnt = that.counter;       // should never appear because of preloading images
    },
    //
    // get wanted metatag value
    //
    getMeta: function (meta, tag) {
      var that = this;
      var nam = that.nam[that.counter];
      var rv;
      //
      // name: special case: filename
      // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist, 
      // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
      //
      switch (tag) {
        case ("name") :         return (nam);                       // Bildname       Sonderfall, nicht aus den Metadaten
        case ("title") :        return (meta.title);                // Titel                        (XMP | IPTC)
        case ("headline") :     return (meta.headline);             // Bildueberschrift             (XMP | IPTC)
        case ("desc") :         return (meta.desc);                 // Beschreibung, Caption        (XMP | IPTC | EXIF)
        case ("rights") :       return (meta.rights);               // Copyright                    (XMP | IPTC | EXIF)
        case ("byline") :       return (meta.byline);               // Künstler                     (IPTC)
        case ("creator") :      return (meta.creator);              // Ersteller                    (XMP)
        case ("date") :         return (meta.date);                 // Aufnahmedatum                (EXIF)
        case ("time") :         return (meta.time);                 // Aufnahmezeit                 (EXIF)
        case ("make") :         return (meta.make);                 // Kamera Hersteller            (EXIF)
        case ("model") :        return (meta.model);                // Kamera Modell                (EXIF)
        case ("artist") :       return (meta.artist);               // Fotograf                     (EXIF)
        case ("userComment") :  return (meta.userComment);          // Benutzer-Kommentar           (EXIF)
        case ("focalLen") :     return (meta.focalLen);             // Objektiv Brennweite          (EXIF)
        case ("fNumber") :      return (meta.fNumber);              // Objektiv Blende              (EXIF)
        case ("subjDist") :     return (meta.subjDist);             // Objektiv Entfernung          (EXIF)
        case ("expTime") :      return (meta.expTime);              // Kamera Belichtungszeit       (EXIF)
        case ("expBias") :      return (meta.expBias);              // Kamera Belichtungskorrektur  (EXIF)
        case ("ISOSpeed") :     return (meta.ISOSpeed);             // Kamera ISO Empfindlichkeit   (EXIF)
        case ("pixelX") :       return (meta.pixelX);               // Kamera Bildbreite            (EXIF)
        case ("pixelY") :       return (meta.pixelY);               // Kamera Bildhoehe             (EXIF)
        case ("latGrad") :      return (meta.latGrad);              // GPS Latitude (Breite)        (EXIF)
        case ("lonGrad") :      return (meta.lonGrad);              // GPS Longitude (Hoehe)        (EXIF)
        case ("ele") :          return (meta.ele);                  // GPS Hoehe                    (EXIF)
        case ("country") :      return (meta.country);              // Land                         (XMP | IPTC)
        case ("province") :     return (meta.province);             // Provinz                      (XMP | IPTC)
        case ("city") :         return (meta.city);                 // Stadt                        (XMP | IPTC)
        case ("location") :     return (meta.location);             // Ort                          (XMP | IPTC)
        case ("rating") :       return (meta.rating);               // Bewertung, z.B: "4"          (XMP)
        case ("label") :        return (meta.label);                // Markierung, z.B: "Gruen"     (XMP)
        case ("exifVers") :     return (meta.exifVers);             // EXIF Version                 (EXIF)
        case ("creaTool") :     return (meta.creaTool);             // Foto-Software, z.B: Lighroom (XMP)
        case ("false") :        return ("");
        case (false) :          return ("");
        default :               alert (strings.metaErr + ": " + tag); return ("");
      }
    },
    //
    // add image title or placeholder (meta == false)
    //
    addTit: function (meta, nr) {
      var that = this;
      var sp20, txt, fig, tit, txtn, imo, num;
      //
      // name: special case: filename
      // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist,  
      // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
      //
      if (! meta) {
        txt = that.hsp;               // utf hardspace
        num = nr;
      } else {
        TitLeft1 = that.getMeta (meta, that.opts.TitLeft1);
        TitLeft2 = that.getMeta (meta, that.opts.TitLeft2);
        if (TitLeft1 != "" && TitLeft2 != "")       TitLeft2 = ", " + TitLeft2;
        TitRight1 = that.getMeta (meta, that.opts.TitRight1);
        TitRight2 = that.getMeta (meta, that.opts.TitRight2);
        if (TitRight1 != "" && TitRight2 != "")     TitRight2 = ", " + TitRight2;
        sp20 = "";
        if (TitRight1 != "" || TitRight2 != "")     sp20 = that.hsp20;    // 10 utf hardspaces
        txt = TitLeft1 + TitLeft2 + sp20 + TitRight1 + TitRight2;
        num = that.counter;
      }
      fig = that.$items[num];
      tit = fig.getElementsByClassName (figTitle)[0];
      if (tit == null || (typeof tit != "object" && typeof tit != "function")) {  // add <div class="figTitle"> if missing
        tit = document.createElement ("div");
        tit.setAttribute ("class", figTitle);
        imo = fig.getElementsByTagName ("img")[0];
        if (imo == null || (typeof imo != "object" && typeof imo != "function"))  return;
        txtn = document.createTextNode (txt);
        tit.appendChild (txtn);
        fig.insertBefore (tit, imo);
      } else {
        if (tit.firstChild == null) {
          txtn = document.createTextNode (txt);
          tit.appendChild (txtn);
        } else {
          tit.firstChild.data = txt;
        }
      }
    },
    //
    // add image caption or placeholder (meta == false)
    //
    addCap: function (meta, nr) {
      var that = this;
      var sp20, txt, fig, capt, txtn, num;
      //
      // name: special case: filename
      // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist, 
      // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
      //
      if (! meta) {
        txt = that.hsp;             // utf hardspace
        num = nr;
      } else {
        CapLeft1 = that.getMeta (meta, that.opts.CapLeft1);
        CapLeft2 = that.getMeta (meta, that.opts.CapLeft2);
        if (CapLeft1 != "" && CapLeft2 != "")       CapLeft2 = ", " + CapLeft2;
        CapRight1 = that.getMeta (meta, that.opts.CapRight1);
        CapRight2 = that.getMeta (meta, that.opts.CapRight2);
        if (CapRight1 != "" && CapRight2 != "")     CapRight2 = ", " + CapRight2;
        sp20 = "";
        if (CapRight1 != "" || CapRight2 != "")     sp20 = that.hsp20;                // 10 utf hardspaces
        txt = CapLeft1 + CapLeft2 + sp20 + CapRight1 + CapRight2;
        num = that.counter;
      }
      fig = that.$items[num];
      capt = fig.getElementsByTagName ("figcaption")[0];
      if (capt == null || (typeof capt != "object" && typeof capt != "function")) {   // add <figcaption> if missing
        capt = document.createElement ("figcaption");
        txtn = document.createTextNode (txt);
        capt.appendChild (txtn);
        fig.appendChild (capt);
      } else {
        if (capt.firstChild == null) {
          txtn = document.createTextNode (txt);
          capt.appendChild (txtn);
        } else {
          capt.firstChild.data = txt;
        }
      }
    },
    //
    // set/reset transition time
    //
    transitionTime: function () {
      var that = this;
      var imo, ims;
      var trans = that.opts.transition;
      if (isNaN (trans) || trans > 3 || trans < 0) {
        trans = 1;                          // default value
      }
      for (i = 0; i < that.numItems; ++i) {
        imo = that.getImage (i);
        if (imo == null)            continue;
        ims = imo.style;
        if (!trans || trans == 0) {         // no transition effekt
          ims.transition = "none";
          ims.WebkitTransition = "none";
        } else {
          ims.transition = "opacity " + trans + "s";
          ims.WebkitTransition = "opacity " + trans + "s";
        }
      }
    },
    //
    // build and inject control icons
    //
    injectControls: function (slidesEl) {
      var that = this;
      //
      // first create all the new elements, we use icons from "Font Awesome"
      //
      var symbPrev =  document.createElement ("i");
      var symbNext =  document.createElement ("i");
      var symbFirst = document.createElement ("i");
      var symbDownl = document.createElement ("i");
      var symbPrint = document.createElement ("i");
      var symbExit =  document.createElement ("i");
      var symbFullS = document.createElement ("i");
      var symbNormS = document.createElement ("i");
      var symbPlay =  document.createElement ("i");
      var symbPause = document.createElement ("i");
      var symbInfo =  document.createElement ("i");
      var symbMap =   document.createElement ("i");
      var symbHelp =  document.createElement ("i");
      //
      var docFrag = document.createDocumentFragment ();
      //
      // add attributes
      //
      symbPrev.setAttribute   ("class", that.icons.pre + that.icons.symPrev);
      symbPrev.setAttribute   ("title", that.icons.tipPrev);
      symbNext.setAttribute   ("class", that.icons.pre + that.icons.symNext);
      symbNext.setAttribute   ("title", that.icons.tipNext);
      symbFirst.setAttribute  ("class", that.icons.pre + that.icons.symFirst);
      symbFirst.setAttribute  ("title", that.icons.tipFirst);
      symbDownl.setAttribute  ("class", that.icons.pre + that.icons.symDownload);
      symbDownl.setAttribute  ("title", that.icons.tipDownload);
      symbPrint.setAttribute  ("class", that.icons.pre + that.icons.symPrint);
      symbPrint.setAttribute  ("title", that.icons.tipPrint);
      symbExit.setAttribute   ("class", that.icons.pre + that.icons.symExit);
      symbExit.setAttribute   ("title", that.icons.tipExit);
      symbFullS.setAttribute  ("class", that.icons.pre + that.icons.symFull);
      symbFullS.setAttribute  ("title", that.icons.tipFull);
      symbNormS.setAttribute  ("class", that.icons.pre + that.icons.symFullOff);
      symbNormS.setAttribute  ("title", that.icons.tipFullOff);
      symbPlay.setAttribute   ("class", that.icons.pre + that.icons.symPlay);
      symbPlay.setAttribute   ("title", that.icons.tipPlay);
      symbPause.setAttribute  ("class", that.icons.pre + that.icons.symPause);
      symbPause.setAttribute  ("title", that.icons.tipPause);
      symbInfo.setAttribute   ("class", that.icons.pre + that.icons.symInfo);
      symbInfo.setAttribute   ("title", that.icons.tipInfo);
      symbMap.setAttribute    ("class", that.icons.pre + that.icons.symMap);
      symbMap.setAttribute    ("title", that.icons.tipMap);
      symbHelp.setAttribute   ("class", that.icons.pre + that.icons.symHelp);
      symbHelp.setAttribute   ("title", that.icons.tipHelp);
      //
      // add classes
      //
      symbPrev.classList.add  ('sym-prev');
      symbNext.classList.add  ('sym-next');
      symbFirst.classList.add ('sym-first');
      symbDownl.classList.add ('sym-download');
      symbPrint.classList.add ('sym-print');
      symbExit.classList.add  ('sym-exit');
      symbInfo.classList.add  ('sym-info');
      symbMap.classList.add   ('sym-map');
      symbHelp.classList.add  ('sym-help');
      symbPlay.classList.add  ('sym-play');          // toggle play | pause
      symbFullS.classList.add ('sym-full');          // toggle full | normal
      //
      // append elements to fragment, then append fragment to DOM
      //
      docFrag.appendChild (symbPrev);
      docFrag.appendChild (symbNext);
      docFrag.appendChild (symbFirst);
      if (that.opts.downloadBut)        docFrag.appendChild (symbDownl);
      if (that.opts.printBut)           docFrag.appendChild (symbPrint);
      if (that.opts.playPauseBut)       docFrag.appendChild (symbPlay);
      if (that.opts.exitBut)            docFrag.appendChild (symbExit);
      if (that.opts.fullScreenBut)      docFrag.appendChild (symbFullS);
      if (that.opts.metaDataBut)        docFrag.appendChild (symbInfo);
      if (that.opts.googleMapsBut)      docFrag.appendChild (symbMap);
      if (that.opts.helpBut)            docFrag.appendChild (symbHelp);
      //
      slidesEl.appendChild (docFrag);
    },
    //
    // define eventhandlers
    //
    addEventListeners: function (slidesEl) {
      var that = this;
      //
      // fullscreenchange handlers
      //
      document.addEventListener ('fullscreenchange', function () {
        that.onFullScreen ('fullscreenchange');
      }, false);
      document.addEventListener ('mozfullscreenchange', function () {
        that.onFullScreen ('mozfullscreenchange');
      }, false);
      document.addEventListener ('webkitfullscreenchange', function () {
        that.onFullScreen ('webkitfullscreenchange');
      }, false);
      document.addEventListener ('MSFullscreenChange', function () {  // Capitals are important !!!
        that.onFullScreen ('MSFullscreenChange');
      }, false);
      document.addEventListener ('ofullscreenchange', function () {
        that.onFullScreen ('ofullscreenchange');
      }, false);
      //
      // eventhandlers for control-icons
      //
      slidesEl.querySelector('.sym-next').addEventListener('click', function () {
        if (that.wait)                            return;
        that.showCurrent (-2);                    // next image
        that.wait = true;
        window.setTimeout (function () {
          that.wait = false;
        }, that.opts.waitProceed);
      }, false);
      //
      slidesEl.querySelector ('.sym-prev').addEventListener ('click', function () {
        if (that.wait)                            return;
        that.showCurrent (-3);                    // previous image
        that.wait = true;
        window.setTimeout (function () {
          that.wait = false;
        }, that.opts.waitProceed);
      }, false);
      //
      slidesEl.querySelector ('.sym-first').addEventListener ('click', function () {
        that.showCurrent (0);                     // 1. image
      }, false);
      //
      if (that.opts.playPauseBut) {               // toggle play pause
        slidesEl.querySelector ('.sym-play').addEventListener ('click', function () {
          that.metaInfo (false);
          that.togglePlayPause (slidesEl);
        }, false);
      }
      //
      if (that.opts.downloadBut) {                // download image
        slidesEl.querySelector ('.sym-download').addEventListener ('click', function () {
          that.metaInfo (false);
          that.downloadBut (slidesEl);
        }, false);
      }
      //
      if (that.opts.printBut) {                   // print image
        slidesEl.querySelector ('.sym-print').addEventListener ('click', function () {
          that.metaInfo (false);
          that.printBut (slidesEl);
        }, false);
      }
      //
      if (that.opts.exitBut) {                    // exit slideshow
        slidesEl.querySelector ('.sym-exit').addEventListener ('click', function () {
          that.metaInfo (false);
          that.exitShow (slidesEl);
        }, false);
      }
      //
      if (that.opts.fullScreenBut) {              // toggle full screen
        slidesEl.querySelector ('.sym-full').addEventListener ('click', function () {
          that.metaInfo (false);
          that.rv = that.toggleFullScreen (mainDiv);
          if (that.traces.fullscreen)             alert (that.rv);
          if (that.rv >= 1 && that.rv <= 5)       slideShow.fullStat = true;    // disables location.reload
          window.setTimeout (function () {                                      // during the setup to fullScreen mode
            slideShow.fullStat = false;
            //that.showCurrent (-1);                // klappt nicht, daher ....
            if (that.counter <= 1) {
              that.showCurrent (-2);
              that.showCurrent (-3);
            } else {
             that.showCurrent (-3);
             that.showCurrent (-2);
            }
          }, 1000);
          if (that.rv >= 6 && that.rv <= 10) {
            if (isNaN (that.counter))             that.counter = 0;
            ++that.counter;
            window.setTimeout (function () {      // empirisch
              window.location.href = slideShow.loc + "?startNr=" + that.counter.toString().padStart(3, '0');
            }, 1000);
          }
        }, false);
      }
      //
      if (that.opts.metaDataBut) {                // image metadata
        slidesEl.querySelector ('.sym-info').addEventListener ('click', function () {
          that.metaInfo (true);
        }, false);
      }
      //
      if (that.opts.googleMapsBut) {              // image location in Google Maps
        slidesEl.querySelector ('.sym-map').addEventListener ('click', function () {
          that.mapInfo ();
        }, false);
      }
      //
      if (that.opts.helpBut) {                    // help, short description
        slidesEl.querySelector ('.sym-help').addEventListener ('click', function () {
          that.helpInfo (true);
        }, false);
      }
      //
      // keybord eventhandler: https://www.javascripter.net/faq/keycodes.htm
      //
      document.onkeyup = function (e) {           // the Print-Key only fires "onkeyup"
        e = e || window.event;
        if (that.traces.keyboard)                 alert (e.keyCode);
        if (e.keyCode == 44) {
          that.metaInfo (false);
          that.printBut (slidesEl);               // print slide with "Print/Drucken"
        }
      }
      //
      document.onkeydown = function (e) {
        var pt, mod, inp, sec, ms, err;
        if (that.wait)                            return;
        e = e || window.event;
        if (that.traces.keyboard)                 alert (e.keyCode);
        if (e.keyCode == 33 || e.keyCode == 37 || e.keyCode == 38) {
          that.showCurrent (-3);                  // previous image
          that.wait = true;
          window.setTimeout (function () {
            that.wait = false;
          }, that.opts.waitProceed);
        } else if (e.keyCode == 34 || e.keyCode == 39 || e.keyCode == 40) {
          that.showCurrent (-2);                  // next image
          that.wait = true;
          window.setTimeout (function () {
            that.wait = false;
          }, that.opts.waitProceed);
        } else if (e.keyCode == 36) {
          that.showCurrent (0);                   // first image with key "Pos1"
        } else if (e.keyCode == 35) {
          that.showCurrent (that.numItems - 1);   // last image with key "Ende"
        } else if (e.keyCode == 32) {
          that.showCurrent (-5);                  // reload image with key "space"
        } else if (e.keyCode == 27) {
          that.metaInfo (false);
          that.exitShow (slidesEl);               // exit slideshow with "Esc"
        } else if (e.keyCode == 68) {
          that.metaInfo (false);
          that.downloadBut (slidesEl);            // download image with "d"
        } else if (e.keyCode == 72) {
          that.helpInfo (true);                   // help, short description with "h"
        } else if (e.keyCode == 88) {
          that.metaInfo (true);                   // image metadata with "x" (xmp)
        } else if (e.keyCode == 83) {
          that.metaInfo (false);
          that.togglePlayPause (slidesEl);        // toggle show (play | pause) with "s"
        //
        // some special keyes
        //
        } else if (e.keyCode == 76) {             // image location in Google Maps with "l"
          that.mapInfo ();
        } else if (e.keyCode == 112 || e.keyCode == 77) {   // F1 || "m": select play mode
          pt = strings.playMod + ":\n----------------------------------\n";
          pt += "0: " + strings.playMod0 + "\n";            // forward (standard)
          pt += "1: " + strings.playMod1 + "\n";            // forward endless
          pt += "2: " + strings.playMod2 + "\n";            // backward
          pt += "3: " + strings.playMod3 + "\n";            // backward endless
          pt += "4: " + strings.playMod4;                   // random slide endless
          while (true) {
            mod = prompt (pt, "");
            switch (mod) {
              case ("0") : that.opts.playMod = 0; that.opts.wrapAround = false; break;
              case ("1") : that.opts.playMod = 0; that.opts.wrapAround = true; break;
              case ("2") : that.opts.playMod = 1; that.opts.wrapAround = false; break;
              case ("3") : that.opts.playMod = 1; that.opts.wrapAround = true; break;
              case ("4") : that.opts.playMod = 2; that.opts.wrapAround = true; break;
              case (null): break;                           // prompt canceled
              default:     continue;
            }
            break;
          }
        } else if (e.keyCode == 113 || e.keyCode == 84) {   // F2 || "t": select display time (speed)
          pt = strings.playTime + " (2 - 20 sec) : ";       // display time in play mode
          for (sec = -1; isNaN (sec) || sec < 2 || sec > 20; ) {
            inp = prompt (pt, "");
            if (inp == null)      break;                    // prompt canceled
            sec = parseInt (inp);
          }
          if (inp != null)        that.opts.speed = sec;
        } else if (e.keyCode == 114 || e.keyCode == 70) {   // F3 || "f": select transition time (fade)
          pt = strings.transTime + " (0 - 3 sec) : ";
          for (sec = -1; isNaN (sec) || sec < 0 || sec > 3; ) {
            inp = prompt (pt, "");
            if (inp == null)      break;                    // prompt canceled
            sec = parseFloat (inp);
          }
          if (inp != null) {
            that.opts.transition = sec;
            that.transitionTime ();
          }
        } else if (e.keyCode == 115 || e.keyCode == 87) {   // F4 || "w": select waiting time
          pt = strings.waitTime + " (200 - 2000 ms) : ";
          for (ms = -1; isNaN (ms) || ms < 200 || ms > 2000; ) {
            inp = prompt (pt, "");
            if (inp == null)      break;                    // prompt canceled
            ms = parseInt (inp);
          }
          if (inp != null)        that.opts.waitProceed = ms;
        }
      };
    },
    //
    // auto-advancing slides
    //
    autoCycle: function (slidesEl, speed, mod) {
      var that = this;
      var interval = window.setInterval (function () {
        switch (mod) {
          case (1): that.showCurrent (-3); break;           // previous image (backward)
          case (2): that.showCurrent (-4); break;           // random image
          default:  that.showCurrent (-2);  break;          // next image (forward)
        }
      }, (speed * 1500));
      return (interval);
    },
    //
    // display image metadata
    //
    metaInfo: function (fl) {
      var that = this;
      if (typeof that.meta[that.counter] != "object")       return;
      //
      // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist, 
      // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
      //
      var vis, visf;
      var nam = that.nam[that.counter];
      var blue = "#AAAAFF";
      var styl = "style='color:" + blue + ";text-decoration:none'";
      var tab = "<span style='position:absolute;left:150px'>";
      var tbe = "</span>";
      var meta = that.meta[that.counter];
      var sp5 = that.hsp5;          // 5 utf hardspaces
      var txt = "<b style='font-size:larger;color:" + blue + "'>" + strings.someMeta + ":</b>";
      if (meta.title != "")         txt += "<br>Title:" +           tab + meta.title + tbe;
      if (meta.headline != "")      txt += "<br>Headline:" +        tab + meta.headline + tbe;
      if (meta.desc != "")          txt += "<br>Description:" +     tab + meta.desc + tbe;
      if (meta.rights != "")        txt += "<br>Copyright:" +       tab + meta.rights + tbe;
      if (meta.date != "")          txt += "<br>Date:" +            tab + meta.date + tbe;
      if (meta.time != "")          txt += "<br>Time:" +            tab + meta.time + tbe;
      if (meta.make != "")          txt += "<br>Make:" +            tab + meta.make + tbe;
      if (meta.model != "")         txt += "<br>Model:" +           tab + meta.model + tbe;
      if (meta.artist != "")        txt += "<br>Artist:" +          tab + meta.artist + tbe;
      if (meta.userComment != "")   txt += "<br>userComment:" +     tab + meta.userComment + tbe;
      if (meta.focalLen != "")      txt += "<br>Focal-Length:" +    tab + meta.focalLen + tbe;
      if (meta.fNumber != "")       txt += "<br>F-Number:" +        tab + meta.fNumber + tbe;
      if (meta.subjDist != "")      txt += "<br>Distance:" +        tab + meta.subjDist + tbe;
      if (meta.expTime != "")       txt += "<br>Exposure-Time:" +   tab + meta.expTime + tbe;
      if (meta.expBias != "")       txt += "<br>Exposure-Bias:" +   tab + meta.expBias + tbe;
      if (meta.ISOSpeed != "")      txt += "<br>ISO-Speed:" +       tab + meta.ISOSpeed + tbe;
      if (meta.pixelX != "")        txt += "<br>X-Pixel:" +         tab + meta.pixelX + tbe;
      if (meta.pixelY != "")        txt += "<br>Y-Pixel:" +         tab + meta.pixelY + tbe;
      if (meta.latGrad != "")       txt += "<br>GPS-Lat-Grad:" +    tab + meta.latGrad + tbe;
      if (meta.lonGrad != "")       txt += "<br>GPS-Lon-Grad:" +    tab + meta.lonGrad + tbe;
      if (meta.ele != "")           txt += "<br>GPS-Elevation:" +   tab + meta.ele + tbe;
      if (meta.country != "")       txt += "<br>Country:" +         tab + meta.country + tbe;
      if (meta.province != "")      txt += "<br>Province:" +        tab + meta.province + tbe;
      if (meta.city != "")          txt += "<br>City:" +            tab + meta.city + tbe;
      if (meta.location != "")      txt += "<br>Location:" +        tab + meta.location + tbe;
      if (meta.rating != "")        txt += "<br>Rating:" +          tab + meta.rating + tbe;
      if (meta.label != "")         txt += "<br>Label:" +           tab + meta.label + tbe;
      if (meta.exifVers != "")      txt += "<br>Exif-Version:" +    tab + meta.exifVers + tbe;
      if (meta.creaTool != "")      txt += "<br>Creation-Tool:" +   tab + meta.creaTool + tbe;
      txt += "<br>";
      txt += "<a href='Javascript:void(null)' onclick='slideShow.getAllExif(\"" + nam + "\")' " + styl + ">";
      txt += "<span class='fa fa-download' /> " + strings.allEXIF + "</a>" + sp5;
      txt += "<a href='Javascript:void(null)' onclick='slideShow.getAllIptc(\"" + nam + "\")' " + styl + ">";
      txt += "<span class='fa fa-download' /> " + strings.allIPTC + "</a>" + sp5;
      txt += "<a href='Javascript:void(null)' onclick='slideShow.getAllXmp(\"" + nam + "\")' " + styl + ">";
      txt += "<span class='fa fa-download' /> " + strings.allXMP + "</a>";
      //txt += "<br>" + that.hsp;
      //
      var el = document.getElementsByClassName (helpMeta);
      if ((typeof el == "object" || typeof el == "function") && el.length == 1) {
        vis = el[0].style.visibility;
        that.helpflag = false;
        that.mapflag = false;
        if (! fl) {
          visf = true;
          that.metaflag = false;
        } else if (! that.metaflag) {
          that.metaflag = true;
          visf = false;
        } else {
          that.metaflag = false;
          visf = (vis == "visible") ? true : false;
        }
        el[0].style.visibility = (visf) ? "hidden" : "visible";
        el[0].style.opacity = 0.8;
        el[0].innerHTML = txt;
      }
    },
    //
    // display help info
    //
    helpInfo: function () {
      var that = this;
      var vis, visf;
      var white = "#FFFFFF";
      var red = "#FF8888";
      var blue = "#AAAAFF";
      var dlcol = (that.opts.downloadBut) ? white : red;        // download image
      var prcol = (that.opts.printBut) ? white : red;           // print image
      var mdcol = (that.opts.metaDataBut) ? white : red;        // image metadata
      var gmcol = (that.opts.googleMapsBut) ? white : red;      // image location in Google Maps
      var excol = (that.opts.exitBut) ? white : red;            // exit slideshow
      var fscol = (that.opts.fullScreenBut) ? white : red;      // toggle full screen
      var plcol = (that.opts.playPauseBut) ? white : red;       // toggle play pause
      var hlcol = (that.opts.helpBut) ? white : red;            // help button
      var sp3 = that.hsp3;                                      // 3 utf hardspaces
      var sp5 = that.hsp5;                                      // 5 utf hardspaces
      var sp10 = that.hsp10;                                    // 10 utf hardspaces
      var tab = "position:absolute;left:50px;font-family:sans-serif;font-style:italic;";
      var txt = "<b style='font-size:larger;color:" + blue + "'>" + strings.shortDesc + ":</b><br>";
      txt += strings.ctrlFunc + " <span style='color:" + red + "'>(" + strings.inhFunc + ")</span>:";
      txt += sp3 + strings.keyFunc;
      txt += "<br><span class='fa fa-download'></span>       <span style='" + tab + "color:" + dlcol + "'>" + strings.tipDownload + sp3 + "[d]</span>";
      txt += "<br><span class='fa fa-print'></span>          <span style='" + tab + "color:" + prcol + "'>" + strings.tipPrint + sp3 + "[Print]</span>";
      txt += "<br><span class='fa fa-backward'></span>       <span style='" + tab + "color'>" + strings.tipPrev + sp3 + "[" + that.cleft + that.cup + "]</span>";
      txt += "<br><span class='fa fa-fast-backward'></span>  <span style='" + tab + "color'>" + strings.tipFirst + sp3 + "[Pos1]</span>";
      txt += "<br><span class='fa fa-camera'></span>         <span style='" + tab + "color:" + mdcol + "'>" + strings.tipInfo + sp3 + "[x]</span>";
      txt += "<br><span class='fa fa-map-marker'></span>     <span style='" + tab + "color:" + gmcol + "'>" + strings.tipMap + sp3 + "[l]</span>";
      txt += "<br><span class='fa fa-external-link'></span>  <span style='" + tab + "color:" + excol + "'>" + strings.tipExit + sp3 + "[Esc]</span>";
      txt += "<br><span class='fa fa-arrows-alt'></span>     <span style='" + tab + "color:" + fscol + "'>" + strings.tipFull + sp3 + "[F11]</span>";
      txt += "<br><span class='fa fa-compress'></span>       <span style='" + tab + "color:" + fscol + "'>" + strings.tipFullOff + sp3 + "[F11]</span>";
      txt += "<br><span class='fa fa-forward'></span>        <span style='" + tab + "color'>" + strings.tipNext + sp3 + "[" + that.cright + that.cdown + "]</span>";
      txt += "<br><span class='fa fa-youtube-play'></span>   <span style='" + tab + "color:" + plcol + "'>" + strings.tipPlay + sp3 + "[s]</span>";
      txt += "<br><span class='fa fa-pause'></span>          <span style='" + tab + "color:" + plcol + "'>" + strings.tipPause + sp3 + "[s]</span>";
      txt += "<br><span class='fa fa-question'></span>       <span style='" + tab + "color:" + hlcol + "'>" + strings.tipHelp + sp3 + "[h]</span>";
      txt += "<br>" + strings.spKey + strings.space + sp3 + strings.tipSame;
      txt += "<br>" + strings.spKey + " [F1 | m] : " + sp3 + strings.playMod;
      txt += "<br>" + strings.spKey + " [F2 | t] : " + sp5 + strings.playTime;
      txt += "<br>" + strings.spKey + " [F3 | f] : " + sp5 + strings.transTime;
      txt += "<br>" + strings.spKey + " [F4 | w] : " + sp3 + strings.waitTime;
      txt += "<br><a href='" + that.opts.scriptDir + "help/help.html' target='_blank' style='color:" + blue + "'>" + strings.about + "</a>";
      txt += sp10 + "<a href='mailto:peter@danninger.eu' style='color:" + blue + "'>" + that.copyright + that.hsp + "peter@danninger.eu</a>";
      txt += sp10 + "<span style='color:" + blue + "'>" + slideShow._version + "</span>";
      //txt += "<br>" + that.hsp;
      //
      var el = document.getElementsByClassName (helpMeta);
      if ((typeof el == "object" || typeof el == "function") && el.length == 1) {
        vis = el[0].style.visibility;
        that.metaflag = false;
        that.mapflag = false;
        if (! that.helpflag) {
          that.helpflag = true;
          visf = false;
        } else {
          that.helpflag = false;
          visf = (vis == "visible") ? true : false;
        }
        el[0].style.visibility = (visf) ? "hidden" : "visible";
        el[0].style.opacity = 0.8;
        el[0].innerHTML = txt;
      }
    },
    //
    // display Google Maps if GPS tags available
    //
    mapInfo: function () {
      var that = this;
      var vis, visf;
      var blue = "#AAAAFF";
      var meta = that.meta[that.counter];
      var lat = parseFloat (meta.latGrad);
      if (meta.latGrad.indexOf ('S') > 0)     lat = -lat;
      var lon = parseFloat (meta.lonGrad);
      if (meta.lonGrad.indexOf ('W') > 0)     lon = -lon;
      var zoom = 15;
      //lat = 48.0646416666667;         // Reference Point Ottobrunn for testing
      //lon = 11.6648333333333;
      var sw = screen.width;
      var sh = screen.height;
      var gmw = 600;                  // GoogleMap width
      var gmh = 400;                  // GoogleMap height
      if (sw < 650) {
        gmw = sw - 150;
        gmh = Math.floor (gmw * 2 / 3);
      }
      if (gmh > (sh - 200)) {
        gmh = sh - 200;
        gmw = Math.floor (gmh * 3 / 2);
      }
      var txt1 = that.hsp5 + "lat: " + meta.latGrad + that.hsp2 + "lon: " + meta.lonGrad;
      var txt = "<b style='font-size:larger;color:" + blue + "'>" + strings.googleMaps + ":</b>";
      if (isNaN (lat) || isNaN (lon)) {
        txt += "<br><br><br>" + strings.noLatLon + txt1 + "<br><br>" + that.hsp;
      } else {
        txt += txt1 + "<br>" + strings.googleURL;
        // click picture to open Google Maps Website
        txt += "<a href='https://maps.google.com/maps?q=" + lat + "," + lon + "' target='_blank'>";
        txt += "<br><img id='gMap' width='" + gmw + "' height='" + gmh + "' src='https://maps.googleapis.com/maps/api/staticmap?";
        // you need a personal Google static maps API key
        txt += "zoom=" + zoom + "&key=" + that.opts.googleMapsKey;
        txt += "&size=" + gmw + "x" + gmh + "&scale=2&maptype=" + that.opts.googleMapsMap;
        txt += "&format=jpg&markers=color:red%7C" + lat + "," + lon + "'></a>";
      }
      //txt += "<br>" + that.hsp;
      //
      var el = document.getElementsByClassName (helpMeta);
      if ((typeof el == "object" || typeof el == "function") && el.length == 1) {
        vis = el[0].style.visibility;
        that.metaflag = false;
        that.helpflag = false;
        if (! that.mapflag) {
          that.mapflag = true;
          visf = false;
        } else {
          that.map = false;
          visf = (vis == "visible") ? true : false;
        }
        el[0].style.visibility = (visf) ? "hidden" : "visible";
        el[0].style.opacity = 0.9;
        el[0].innerHTML = txt;
        el[0].style.minWidth = gmw + 10 + "px";
      }
    },
    //
    // exit slideshow
    //
    exitShow: function (slidesEl) {
      var that = this;
      // remove .show from the last recent item
      if (that.oldcnt >= 0) {
        that.$items[that.oldcnt].classList.remove ('show');
      }
      if (that.opts.exitURL != "") {
        window.location.replace (that.opts.exitURL);    // special case
      } else {
        window.close ();
      }
    },
    //
    // download image
    //
    downloadBut: function (slidesEl) {
      var that = this;
      var pic, url, lnk, nam;
      var figure = that.$items[that.counter];
      if (typeof figure == "object" && figure != null) {
        pic = figure.getElementsByTagName ("img")[0];
        if (typeof pic == "object" && pic != null) {
          url = pic.getAttribute ("src");
          if (that.opts.oriPicDir && that.opts.oriPicDir != "") {
            url = url.replace (that.opts.picDir, that.opts.oriPicDir);
          }
          lnk = document.createElement ('a');
          nam = url.substring (url.lastIndexOf ('/') + 1);
          lnk.style.visibility = 'hidden';
          lnk.href = url;
          lnk.download = nam;
          document.body.appendChild (lnk);
          lnk.click ();
          document.body.removeChild (lnk);
        }
      }
    },
    //
    // print image
    //
    printBut: function (slidesEl) {
      var that = this;
      var pic, url, w, pw, ph;
      var figure = that.$items[that.counter];
      if (typeof figure == "object" && figure != null) {
         pic = figure.getElementsByTagName ("img")[0];
        if (typeof pic == "object" && pic != null) {
          url = pic.getAttribute ("src");
          if (that.opts.oriPicDir && that.opts.oriPicDir != "") {
            url = url.replace (that.opts.picDir, that.opts.oriPicDir);
          }
          pw = pic.width;
          ph = pic.height;
          //alert ("click OK\n\nthen print image with ^P\n\nthen close window with ^W");    // falls w.print() nicht klappt
          w = window.open (url, "printWin", "innerWidth=" + pw + ",innerHeight=" + ph + "," +
                "location=no,directories=no,menubar=no,status=no,toolbar=no,resizeable=no,scrollable=no,copyHistory=no,fullscreen=yes");
          if (w == null)      return;                 // should not happen
          w.onload = new function () {
            window.setTimeout (function () {
              w.print ();
              w.close ();
            }, 1000);
          }
        }
      }
    },
    //
    // support swiping on touch devices, requires hammer.js
    //
    addSwipe: function (slidesEl) {
      var that = this;
      var swipe = new Hammer (slidesEl);
      swipe.on ('swiperight', function (e) {
        if (that.traces.swipe)                alert ("swiperight");
        that.showCurrent (-3);                // previous image
      });
      swipe.on ('swipeleft', function (e) {
        if (that.traces.swipe)                alert ("swipeleft");
        that.showCurrent (-2);                // next image
      });
      // doesn't work as expected, mouse click fires 'tap' :-(
      /*
      swipe.on ('tap', function (e) {
        if (that.traces.swipe)                alert ("tap");
        that.showCurrent (-5);                // reload image
      });
      */
    },
    //
    // toggle play pause (autoadvancing slides)
    //
    togglePlayPause: function (slidesEl) {
      var that = this;
      var mod = 0;
      var pt = strings.playMod + ": \n----------------------------------\n";    // play modus
      pt += "0: " + strings.playMod0 + "\n";      // forward (standard)
      pt += "1: " + strings.playMod1 + "\n";      // forward endless
      pt += "2: " + strings.playMod2 + "\n";      // backward
      pt += "3: " + strings.playMod3 + "\n";      // backward endless
      pt += "4: " + strings.playMod4;             // random slide endless
      var symbol, oldsym, titel;
      if (! that.playing) {
        if (that.opts.playMod == 3) {             // 3: askMe
          mod = prompt (pt, "");
          switch (mod) {
            case ("0") : that.playMod = 0; that.opts.wrapAround = false; break;
            case ("1") : that.playMod = 0; that.opts.wrapAround = true; break;
            case ("2") : that.playMod = 1; that.opts.wrapAround = false; break;
            case ("3") : that.playMod = 1; that.opts.wrapAround = true; break;
            case ("4") : that.playMod = 2; that.opts.wrapAround = true; break;
            default    : that.playMod = 0; that.opts.wrapAround = false;
          }
        } else {                                  // 0: forward, 1: backward, 2: random
          that.playMod = that.opts.playMod;
        }
        that.interval = that.autoCycle (that.slidesEl, that.opts.speed, that.playMod);
        that.playing = true;
        symbol = that.icons.symPause;
        oldsym = that.icons.symPlay;
        titel = that.icons.tipPause;
      } else {
        if (that.interval) {
          window.clearInterval (that.interval);
        }
        that.playing = false;
        that.interval = false;
        that.opts.wrapAround = that.wrapAroundBak;
        symbol = that.icons.symPlay;
        oldsym = that.icons.symPause;
        titel = that.icons.tipPlay;
      }
      var el = document.getElementsByClassName ("sym-play");  // Safari: "function"
      if ((typeof el == "object" || typeof el == "function") && el.length == 1) {
        el[0].classList.remove (oldsym);
        el[0].classList.add (symbol);
        el[0].title = titel;
      }
    },
    //
    // toggle fullscreen modus
    //
    toggleFullScreen: function (mainDiv) {
      var el = document.getElementsByClassName (mainDiv);     // Safari: "function"
      if ((typeof el != "object" && typeof el != "function") || el.length != 1)      return (-1);
      if (!document.fullscreenElement &&
          !document.mozFullScreenElement &&                   // S is important !!!
          !document.webkitFullscreenElement &&
          !document.msFullscreenElement &&
          !document.oFullscreenElement) {
        if (document.documentElement.requestFullscreen) {
          el[0].requestFullscreen ();
          return (1);
        } else if (document.documentElement.mozRequestFullScreen) {
          el[0].mozRequestFullScreen ();                      // S is important !!!
          return (2);
        } else if (document.documentElement.msRequestFullscreen) {
          el[0].msRequestFullscreen ();
          return (3);
        } else if (document.documentElement.oRequestFullscreen) {
          el[0].oRequestFullscreen ();
          return (4);
        } else if (document.documentElement.webkitRequestFullscreen) {
          el[0].webkitRequestFullscreen (el[0].ALLOW_KEYBOARD_INPUT);
          //el[0].webkitRequestFullscreen ();
          return (5);
        } else {
          alert (strings.noFullScr + " :-(");
          return (-2);
        }
      } else {
        if (document.exitFullscreen) {
          document.exitFullscreen ();
          return (6);
        } else if (document.mozCancelFullScreen) {
          document.mozCancelFullScreen ();                    // S is important !!!
          return (7);
        } else if (document.msExitFullscreen) {
          document.msExitFullscreen ();
          return (8);
        } else if (document.oExitFullscreen) {
          document.oExitFullscreen ();
          return (9);
        } else if (document.webkitExitFullscreen) {
          // Chrome on Android has a big problem :-(
          document.webkitExitFullscreen ();
          return (10);
        } else {
          alert (strings.noFullScr + " :-(");
          return (-3);
        }
      }
    },
    //
    // full screen eventhandler
    //
    onFullScreen: function (ev) {
      var that = this;
      var symbol, oldsym, titel, vp;
      if (that.traces.fullscreen)                 alert ("onFullScreen: " + ev);
      if (!document.fullscreenElement &&
          !document.mozFullScreenElement &&                 // S is important !!!
          !document.webkitFullscreenElement &&
          !document.msFullscreenElement &&
          !document.oFullscreenElement) {
        symbol = that.icons.symFull;
        oldsym = that.icons.symFullOff;
        titel = that.icons.tipFull;
        // doesn't help with the Chrome-Android problem :-(
        // if (ev == "webkitfullscreenchange") {
          // slideShow.resizeShow (classNam, true)
          // vp = document.querySelector ("meta[name=viewport]");
          // vp.setAttribute ("content", "width=device-width, initial-scale=1");
        // }
      } else {
        symbol = that.icons.symFullOff;
        oldsym = that.icons.symFull;
        titel = that.icons.tipFullOff;
      }
      var el = document.getElementsByClassName ("sym-full");
      if (typeof el == "object" && el.length == 1) {
        el[0].classList.remove (oldsym);
        el[0].classList.add (symbol);
        el[0].title = titel;
      }
    }
  }; // end of Slide_Show object .....
  //
  //
  // make instances of slideshows as needed .... but I do support only one slideShow
  //
  // [].forEach.call ($slideshows, function (slidesEl) {       // div elements of class "slides"
  //   $slideshow = Object.create (Slide_Show);
  //   $slideshow.init (slidesEl, opts);
  // });
  //
  // I do support only one slideShow
  //
  $slideshow = Object.create (Slide_Show);
  var slel = document.getElementsByClassName (slidesDiv)[0];
  if (slel == null || (typeof slel != "object" && typeof slel != "function")) {   // error
    alert (strings.noSlides + " :-(");
    return;
  }
  $slideshow.init (slel, opts);
}; // end of slideShow.makeShow ()
//
//------------------------------------------------------------------------------

//
// resize elements depending on screensize / imagesize
// optimize "responsiveness" of slideshow
//
slideShow.resizeShow = function (classNam, par) {
  var i, l, h, figs, fig, imgs, img, ptit, caps, cap, ftits, ftit, syms, sym, top, bottom, iwm, ihm, hlpmeta, gmap;
  var pageTitle = (typeof classNam.pageTitle == "undefined") ? "pageTitle" : classNam.pageTitle;    // pageTitle div class
  var figTitle = (typeof classNam.figTitle == "undefined") ? "figTitle" : classNam.figTitle;        // figTitle div class
  var helpMeta = (typeof classNam.helpMeta == "undefined") ? "helpMeta" : classNam.helpMeta;        // helpMeta div class
  var ptith = 30;                 // default page-title-height at big screen
  var ftith = 30;                 // default image-title-height at big screen
  var caph = 30;                  // default image-caption-height at big screen
  var fnts = 24;                  // default title & caption font-size at big screen
  var fntsi = 16;                 // default info font-size at big screen
  var symw = 36;                  // default symbol-width at big screen
  var symh = 36;                  // default symbol-height at big screen
  var symfs = 28;                 // default symbol-font-size at big screen
  var symmt = 64;                 // default symbol-margin-top (bottom) at big screen
  var pad = 14;                   // padding reserve
  var smax = 1600;                // resize elements on lower screen-diagonales
  var smin = 600;                 //  .... but only until this lower limit
  var fact = fact1 = 1;           // resizing factors
  var swi = screen.width;
  var shg = screen.height;
  var wiw = window.innerWidth;
  var wih = window.innerHeight;
  var dcw = document.documentElement.clientWidth;
  var dch = document.documentElement.clientHeight;
  var wwi = (dcw > wiw) ? dcw : wiw;
  var whg = (dch > wih) ? dch : wih;
  if (wwi < 100 && swi > wwi)     wwi = swi;
  if (whg < 100 && shg > whg)     whg = shg;
  var diag = Math.floor (Math.sqrt ((wwi * wwi) + (whg * whg)));
  if (diag < 100) {
    //slideShow.resizeShow (classNam, true)      // tray again
    alert (strings.tooSmall + " :-(" + diag);
    return;
  }
  //
  // resize the default values (here the show becomes responsiv)
  //
  if (diag < smax) {
    if (diag < smin)            diag = smin;
    fact1 = diag / smax;
    fact = Math.sqrt (fact1);
    ptith = Math.floor (ptith * fact);
    ftith = Math.floor (ftith * fact);
    caph = Math.floor (caph * fact);
    fnts = Math.floor (fnts * fact);
    fntsi = Math.floor (fntsi * fact);
    //symw = Math.floor (symw * fact);
    //symh = Math.floor (symh * fact);
    //symfs = Math.floor (symfs * fact);
    symmt = Math.floor (symmt * fact);
  }
  //
  // resize <p class="pageTitle"> (common title on top)
  //
  ptit = document.getElementsByClassName (pageTitle)[0];
  if (typeof ptit != "object" && typeof ptit != "function") {     // pageTitle is optional
    ptith = 0;
  } else {
    ptit.style.height = ptith + "px";
    ptit.style.fontSize = fnts + "px";
  }
  //
  // resize all <figure> (images with optional title and caption)
  //
  figs = document.getElementsByTagName ("figure");
  l = figs.length;
  for (i = 0; i < l; ++i ) {
    fig = figs[i];
    if (typeof fig == "object" || typeof fig == "function") {
      fig.style.width = wwi + "px";
      fig.style.height = (whg - ptith) + "px";       // pageTitle is optional
    }
  }
  //
  // resize all <div class="figTitle"> (image title on top)
  //
  ftits = document.getElementsByClassName (figTitle);
  if (typeof ftits != "object" && typeof ftits != "function") {
    ftith = 0;
  } else {
    l = ftits.length;
    for (i = 0; i < l; ++i ) {
      ftit = ftits[i];
      if (typeof ftit == "object" || typeof ftit == "function") {
        ftit.style.height = ftith + "px";
        ftit.style.fontSize = fnts + "px";
      }
    }
  }
  //
  // resize all <figcaption> (image caption below)
  //
  caps = document.getElementsByTagName ("figcaption");
  if (typeof caps != "object" && typeof caps != "function") {
    caph = 0;
  } else {
    l = caps.length;
    for (i = 0; i < l; ++i ) {
      cap = caps[i];
      if (typeof cap == "object" || typeof cap == "function") {
        cap.style.height = caph + "px";
        cap.style.fontSize = fnts + "px";
      }
    }
  }
  //
  // set maxWidth and maxHeight for all <img>
  // image enlarging is done only for active image in showCurrent ()
  //
  imgs = document.getElementsByTagName ("img");
  l = imgs.length;
  for (i = 0; i < l; ++i ) {
    img = imgs[i];
    if (typeof img == "object" || typeof img == "function") {
      iwm = wwi - symw - symw - pad;              // symbols left & right
      ihm = whg - ptith - ftith - caph - pad;     // title & caption
      img.style.maxWidth = iwm + "px";
      img.style.maxHeight = ihm + "px";
    }
  }
  //
  // resize class helpMeta (meta tags)
  //
  hlpmeta = document.getElementsByClassName (helpMeta)[0];
  if (typeof hlpmeta != "object" && typeof hlpmeta != "function") {   // info is optional
    return;
  } else {
    hlpmeta.style.fontSize = fntsi + "px";
    hlpmeta.style.left = symw + 8 + "px";
    hlpmeta.style.top = ((shg < 400) ? 0 : ftith) + 4 + "px";
  }
  //
  // resize all <i> control symbols
  //
  syms = document.getElementsByTagName ("i");
  l = syms.length;
  for (i = 0; i < l; ++i ) {
    sym = syms[i];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.width = symw + "px";                                  // symbols
      sym.style.height = symh + "px";
      sym.style.fontSize = symfs + "px";
    }
  }
  //
  // align symbols depending on image-size
  //
  syms = new Array ("sym-download", "sym-print", "sym-exit", "sym-full");   // symbols on top
  top = Math.floor (ptith + ftith);
  l = syms.length;
  for (i = 0; i < l; ++i ) {
    sym = document.getElementsByClassName (syms[i])[0];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.top = top + "px";
    }
  }
  //
  syms = new Array ("sym-prev", "sym-first", "sym-next", "sym-play");       // symbols in the middle
  top = Math.floor ((whg / 2) - symmt);
  l = syms.length;
  for (i = 0; i < l; ++i ) {
    sym = document.getElementsByClassName (syms[i])[0];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.top = top + "px";
    }
  }
  //
  syms = new Array ("sym-print", "sym-full", "sym-first", "sym-play");      // symbols with marginTop
  l = syms.length;
  for (i = 0; i < l; ++i ) {
    sym = document.getElementsByClassName (syms[i])[0];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.marginTop = symmt + "px";
    }
  }
  //
  syms = new Array ("sym-info", "sym-map", "sym-help");                     // symbols in the bottom
  l = syms.length;
  bottom = Math.floor (caph);
  for (i = 0; i < l; ++i ) {
    sym = document.getElementsByClassName (syms[i])[0];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.bottom = bottom + "px";
    }
  }
  syms = new Array ("sym-map");                                             // symbols with marginBottom
  l = syms.length;
  for (i = 0; i < l; ++i ) {
    sym = document.getElementsByClassName (syms[i])[0];
    if (typeof sym == "object" || typeof sym == "function") {
      sym.style.marginBottom = symmt + "px";
    }
  }
  //
  // special case: resize display when enlarged image shown
  //
  if (par && slideShow.enlarge) {
    // actually not active
  }
};

// 
// EXIF dateTime String in Sekunden seit 1970 wandeln, negativ wenn ungültig
// z.B.   2015:08:11 10:58:08
//
slideShow.date2sec = function (dateTime) {
  var date, sec;
  var timeA = new Array ();
  var un = "undefined";
  if (typeof dateTime == un || dateTime == un)    return (-1);      // ungültiger Datum-Zeit-String
  if (dateTime.length != 19)                      return (-2);      // ungültiger Datum-Zeit-String
  dateTime = dateTime.replace (' ', ':');
  timeA = dateTime.split (':');
  if (timeA.length != 6)                          return (-3);      // ungültiger Datum-Zeit-String
  date = new Date (timeA[0], timeA[1], timeA[2], timeA[3], timeA[4], timeA[5]);
  sec = Math.floor (date.getTime () / 1000);                        // Sekunden (seit 1.1.1970)
  if (sec <= 0)                                   return (-4);      // ungültiger Datum-Zeit-String
  return (sec);
}

//
// get selected metatags from image
// XMP overrides IPTC overrides EXIF
// needs metatags.js and exif.js
//
slideShow.getMetaTags = function (img, callback) {
  if (typeof img != "object" && typeof img != "function") {
    if (callback) {
      callback (false);           // callback
    }
    return;
  }
  metatags.readMetaData (img, function () {         // callback from metatags.js
    var title, headline, desc, byline, creator, rights, date, time, dto, dtc, dtd;
    var exifVers, make, model, artist, userComment, creaTool;
    var focalLen, fNumber, subjDist;
    var expTime, expBias, ISOSpeed;
    var pixelX, pixelY;
    var latGrad, lonGrad, ele;
    var country, province, city, location;
    var rating, label;
    var dateA = new Array ();
    var un = "undefined";
    var ob = "object";
    var st = "string";
    var degree = "\u00b0";                          // degree symbol
    //
    // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist, 
    // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
    //
    title = headline = desc = rights = date = time = make = model = artist = userComment
          = focalLen = fNumber = subjDist = expTime = expBias = ISOSpeed = pixelX = pixelY = latGrad = lonGrad = ele
          = country = province = city = location = rating = label = exifVers = creaTool = "";
    //
    if (typeof metatags._EXIF.exifVers != un && metatags._EXIF.exifVers != un)        exifVers = metatags._EXIF.exifVers;       // EXIF Version
    if (typeof metatags._EXIF.make != un && metatags._EXIF.make != un)                make = metatags._EXIF.make;               // EXIF Hersteller
    if (typeof metatags._EXIF.model != un && metatags._EXIF.model != un)              model = metatags._EXIF.model;             // EXIF Kamera
    if (typeof metatags._EXIF.artist != un && metatags._EXIF.artist != un)            artist = metatags._EXIF.artist;           // EXIF Fotograf (Lightroom)
    if (typeof metatags._EXIF.userComment != un && metatags._EXIF.userComment != un)  userComment = metatags._EXIF.userComment; // EXIF Benutzer-Kommentar (Lightroom)
    //
    if (typeof metatags._EXIF.focalLen != un && metatags._EXIF.focalLen != un)        focalLen = metatags._EXIF.focalLen;       // EXIF Objektiv Brennweite
    if (focalLen != "")       focalLen += " mm";
    if (typeof metatags._EXIF.fNumber != un && metatags._EXIF.fNumber != un)          fNumber = metatags._EXIF.fNumber;         // EXIF Objektiv Blende
    if (typeof metatags._EXIF.subjDist != un && metatags._EXIF.subjDist != un)        subjDist = metatags._EXIF.subjDist;       // EXIF Objektiv Entfernung
    if (subjDist != "" && subjDist != "infinity" && subjDist != "unknown")            subjDist += " m";
    //
    if (typeof metatags._EXIF.expTime != un && metatags._EXIF.expTime != un)          expTime = metatags._EXIF.expTime;         // EXIF Kamera Belichtungszeit
    var expt = parseFloat (expTime);
    var rezv;
    if (!isNaN (expt) && expt <= 0.5) {
      rezv = 1 / expt;
      if (rezv < 10)    rezv = Math.floor (rezv * 10) / 10;
      else              rezv = Math.floor (rezv);
      expTime += "   (1/" + rezv + " sec)";
    }
    if (typeof metatags._EXIF.expBias != un && metatags._EXIF.expBias != un)      expBias = metatags._EXIF.expBias;     // EXIF Kamera Belichtungskorrektur
    if (typeof metatags._EXIF.ISOSpeed != un && metatags._EXIF.ISOSpeed != un)    ISOSpeed = metatags._EXIF.ISOSpeed;   // EXIF Kamera ISO Empfindlichkeit
    if (typeof metatags._EXIF.pixelX != un && metatags._EXIF.pixelX != un)        pixelX = metatags._EXIF.pixelX;       // EXIF Kamera Bildbreite
    if (typeof metatags._EXIF.pixelY != un && metatags._EXIF.pixelY != un)        pixelY = metatags._EXIF.pixelY;       // EXIF Kamera Bildhoehe
    //
    if (typeof metatags._EXIF.latGrad != un && metatags._EXIF.latGrad != un)      latGrad = metatags._EXIF.latGrad;     // EXIF GPS Latitude
    latGrad = (latGrad == "") ? "" : (latGrad >= 0) ? (latGrad + degree + " N") : ((-latGrad) + degree + " S");
    if (typeof metatags._EXIF.lonGrad != un && metatags._EXIF.lonGrad != un)      lonGrad = metatags._EXIF.lonGrad;     // EXIF GPS Longitude
    lonGrad = (lonGrad == "") ? "" : (lonGrad >= 0) ? (lonGrad + degree + " E") : ((-lonGrad) + degree + " W");
    if (typeof metatags._EXIF.ele != un && metatags._EXIF.ele != un)              ele = metatags._EXIF.ele;             // EXIF GPS Hoehe
    if (ele != "")       ele += " m";
    //
    if (typeof metatags._XMP.title != un && metatags._XMP.title != un && metatags._XMP.title != "")
                                                                                  title = metatags._XMP.title;          // Titel (Lightroom)
    else if (typeof metatags._IPTC.title != un && metatags._IPTC.title != un)     title = metatags._IPTC.title;
    title = slideShow.cleanCode (title);
    title = slideShow.cleanTxt (title);
    if (typeof metatags._IPTC.byline != un && metatags._IPTC.byline != un)        byline = metatags._IPTC.byline;
    if (typeof metatags._XMP.creator != un && metatags._XMP.creator != un)        creator = metatags._XMP.creator;
    
    if (typeof metatags._XMP.creaTool != un && metatags._XMP.creaTool != un)      creaTool = metatags._XMP.creaTool;    // Foto-Software, z.B: Lighroom
    if (typeof metatags._XMP.rating != un && metatags._XMP.rating != un)          rating = metatags._XMP.rating;        // Bewertung, z.B: "4"
    if (typeof metatags._XMP.label != un && metatags._XMP.label != un)            label = metatags._XMP.label;          // Markierung, z.B: "Gruen"
    label = slideShow.cleanCode (label);
    if (typeof metatags._XMP.headline != un && metatags._XMP.headline != un && metatags._XMP.headline != "")
                                                                                  headline = metatags._XMP.headline;    // Bildueberschrift (Lightroom)
    else if (typeof metatags._IPTC.headline!=un && metatags._IPTC.headline!=un)   headline = metatags._IPTC.headline;
    headline = slideShow.cleanCode (headline);
    headline = slideShow.cleanTxt (headline);
    if (typeof metatags._XMP.desc != un && metatags._XMP.desc != un && metatags._XMP.desc != "")
                                                                                  desc = metatags._XMP.desc;            // Beschreibung (Lightroom)
    else if (typeof metatags._IPTC.desc != un && metatags._IPTC.desc != un && metatags._IPTC.desc != "")
                                                                                  desc = metatags._IPTC.desc;
    else if (typeof metatags._EXIF.desc != un && metatags._EXIF.desc != un)       desc = metatags._EXIF.desc;
    desc = slideShow.cleanCode (desc);
    desc = slideShow.cleanTxt (desc);
    if (typeof metatags._XMP.rights != un && metatags._XMP.rights != un && metatags._XMP.rights != "")
                                                                                  rights = metatags._XMP.rights;        // Copyright (Lightroom)
    else if (typeof metatags._IPTC.rights != un && metatags._IPTC.rights != un && metatags._IPTC.rights != "")
                                                                                  rights = metatags._IPTC.rights;
    else if (typeof metatags._EXIF.rights != un && metatags._EXIF.rights != un)   rights = metatags._EXIF.rights;
    //
    dto = slideShow.date2sec (metatags._EXIF.dateTimeOriginal);                                                         // Aufnahmedatum und Zeit
    dtd = slideShow.date2sec (metatags._EXIF.dateTimeDigital);                                                          // Digitalisierungsdatum und Zeit
    dtc = slideShow.date2sec (metatags._EXIF.dateTime);                                                                 // Änderungsdatum und Zeit
    if (dto > 0)                                            date = metatags._EXIF.dateTimeOriginal; 
    if (dtd > 0 && (dto < 0 || (dto > 0 && dtd < dto)))     date = metatags._EXIF.dateTimeDigital;  
    if (dtc > 0 && dto < 0 && dtd < 0)                      date = metatags._EXIF.dateTime;
    if (date.length == 19) {                                                                                            // gültiger EXIF-Zeitstempel
      dateA = date.split (" ");
      time = dateA[1];
      dateA = dateA[0].split (":");
      dateA.reverse ();
      date = dateA.join (".");
    }
    //
    if (typeof metatags._XMP.country != un && metatags._XMP.country != un && metatags._XMP.country != "")
                                                                                  country = metatags._XMP.country;      // Land (Lightroom)
    else if (typeof metatags._IPTC.country != un && metatags._IPTC.country != un) country = metatags._IPTC.country;
    country = slideShow.cleanCode (country);
    country = slideShow.cleanTxt (country);
    //
    if (typeof metatags._XMP.province != un && metatags._XMP.province != un && metatags._XMP.province != "")
                                                                                  province = metatags._XMP.province;    // Provinz (Lightroom)
    else if (typeof metatags._IPTC.province!=un && metatags._IPTC.province!=un)   province = metatags._IPTC.province;
    province = slideShow.cleanCode (province);
    province = slideShow.cleanTxt (province);
    //
    if (typeof metatags._XMP.city != un && metatags._XMP.city != un && metatags._XMP.city != "")
                                                                                  city = metatags._XMP.city;            // Stadt (Lightroom)
    else if (typeof metatags._IPTC.city != un && metatags._IPTC.city != un)       city = metatags._IPTC.city;
    city = slideShow.cleanCode (city);
    city = slideShow.cleanTxt (city);
    //
    if (typeof metatags._XMP.location != un && metatags._XMP.location != un && metatags._XMP.location != "")
                                                                                  location = metatags._XMP.location;    // Location (Lightroom)
    else if (typeof metatags._IPTC.location != un && metatags._IPTC.location != un)   location = metatags._IPTC.location;
    location = slideShow.cleanCode (location);
    location = slideShow.cleanTxt (location);
    //
    if (city == "" && location != "")           city = location;                // evtl. Korrektur
    if (city == "" && province != "")           city = province;                // evtl. Korrektur
    //
    // selected metadata (XMP overrides IPTC overrides EXIF)
    //
    // title, headline, desc, byline, creator, rights, date, time, make, model, artist, userComment, focalLen, fNumber, subjDist, 
    // expTime, expBias, ISOSpeed, pixelX, pixelY, latGrad, lonGrad, ele, country, province, city, location, rating, label, exifVers, creaTool
    //
    var meta = {
      title:        title,            // Titel                        (XMP | IPTC)
      headline:     headline,         // Bildueberschrift             (XMP | IPTC)
      desc:         desc,             // Beschreibung, Caption        (XMP | IPTC | EXIF)
      byline:       byline,           // Künstler                     (IPTC)
      creator:      creator,          // Ersteller                    (XMP)
      rights:       rights,           // Copyright                    (XMP | IPTC | EXIF)
      date:         date,             // Aufnahmedatum                (EXIF)
      time:         time,             // Aufnahmezeit                 (EXIF)
      make:         make,             // Kamera Hersteller            (EXIF)
      model:        model,            // Kamera Modell                (EXIF)
      artist:       artist,           // Fotograf                     (EXIF)
      userComment:  userComment,      // Benutzer-Kommentar           (EXIF)
      focalLen:     focalLen,         // Objektiv Brennweite          (EXIF)
      fNumber:      fNumber,          // Objektiv Blende              (EXIF)
      subjDist:     subjDist,         // Objektiv Entfernung          (EXIF)
      expTime:      expTime,          // Kamera Belichtungszeit       (EXIF)
      expBias:      expBias,          // Kamera Belichtungskorrektur  (EXIF)
      ISOSpeed:     ISOSpeed,         // Kamera ISO Empfindlichkeit   (EXIF)
      pixelX:       pixelX,           // Kamera Bildbreite            (EXIF)
      pixelY:       pixelY,           // Kamera Bildhoehe             (EXIF)
      latGrad:      latGrad,          // GPS Latitude (Breite)        (EXIF)
      lonGrad:      lonGrad,          // GPS Longitude (Hoehe)        (EXIF)
      ele:          ele,              // GPS Hoehe                    (EXIF)
      country:      country,          // Land                         (XMP | IPTC)
      province:     province,         // Provinz                      (XMP | IPTC)
      city:         city,             // Stadt                        (XMP | IPTC)
      location:     location,         // Ort                          (XMP | IPTC)
      rating:       rating,           // Bewertung, z.B: "4"          (XMP)
      label:        label,            // Markierung, z.B: "Gruen"     (XMP)
      exifVers:     exifVers,         // EXIF Version                 (EXIF)
      creaTool:     creaTool,         // Foto-Software, z.B: Lighroom (XMP)
    }
    if (callback) {
      callback (meta);                // callback
    }
  });
};
//
// download all metadata (exif, iptc, xmp)
//
slideShow.destroyClickedElement = function (event) {
  document.body.removeChild (event.target);
}

slideShow.getAllExif = function (nam) {
  if (typeof metatags._EXIF.exifAll != "string" || metatags._EXIF.exifAll == "")    return;
  var i = nam.lastIndexOf (".");
  if (i > 0)      nam = nam.substring (0, i);
  var textFileAsBlob = new Blob ([metatags._EXIF.exifAll], {type:'text/plain'});
  var saveFileName = nam + "_EXIF.txt";           // preset filename
  var downloadLink = document.createElement("a");
  downloadLink.download = saveFileName;
  downloadLink.innerHTML = strings.download;      // Download File
  downloadLink.href = window.URL.createObjectURL (textFileAsBlob);
  downloadLink.onclick = slideShow.destroyClickedElement;
  downloadLink.style.display = "none";
  document.body.appendChild (downloadLink);
  downloadLink.click ();
};

slideShow.getAllIptc = function (nam) {
  if (typeof metatags._EXIF.iptcAll != "string" || metatags._EXIF.iptcAll == "")    return;
  var i = nam.lastIndexOf (".");
  if (i > 0)      nam = nam.substring (0, i);
  var textFileAsBlob = new Blob ([metatags._EXIF.iptcAll], {type:'text/plain'});
  var saveFileName = nam + "_IPTC.txt";           // preset filename
  var downloadLink = document.createElement ("a");
  downloadLink.download = saveFileName;
  downloadLink.innerHTML = strings.download;      // Download File
  downloadLink.href = window.URL.createObjectURL (textFileAsBlob);
  downloadLink.onclick = slideShow.destroyClickedElement;
  downloadLink.style.display = "none";
  document.body.appendChild (downloadLink);
  downloadLink.click ();
};

slideShow.getAllXmp = function (nam) {
  if (typeof metatags._EXIF.xmpAll != "string" || metatags._EXIF.xmpAll == "")    return;
  var i = nam.lastIndexOf (".");
  if (i > 0)      nam = nam.substring (0, i);
  var textFileAsBlob = new Blob ([metatags._EXIF.xmpAll], {type:'text/plain'});
  var saveFileName = nam + "_XMP.txt";           // preset filename
  var downloadLink = document.createElement ("a");
  downloadLink.download = saveFileName;
  downloadLink.innerHTML = strings.download;      // Download File
  downloadLink.href = window.URL.createObjectURL (textFileAsBlob);
  downloadLink.onclick = slideShow.destroyClickedElement;
  downloadLink.style.display = "none";
  document.body.appendChild (downloadLink);
  downloadLink.click ();
};
//
// correct unvalid HTML-Entities from name, cmt, desc (Lightroom-Fehler)
//
slideShow.cleanTxt = function (t) {
  var s = t;
  while (s.indexOf ("&amp;") >= 0)      s = s.replace ("&amp;", "&");
  while (s.indexOf ("&lt;") >= 0)       s = s.replace ("&lt;", "<");
  while (s.indexOf ("&gt;") >= 0)       s = s.replace ("&gt;", ">");
  return (s);
};
//
// convert code to ASCII for name, cmt, desc
//
slideShow.cleanCode = function (t) {
  var s = t;
  s = escape (t);
  s = decodeURIComponent (s);
  //for (var i = 0; i < s.length; ++i)        alert (s[i] + "   " + s.charCodeAt (i).toString (16));
  return (s);
};
//
// the end
//
